struct Dog { breed string } struct Cat { mut: breed string } fn new_cat(breed string) Cat { return Cat{breed} } fn (c &Cat) name() string { if c.breed != '' { assert c.breed == 'Persian' } return 'Cat' } fn (c &Cat) speak(s string) { if c.breed != '' { assert c.breed == 'Persian' } assert s == 'Hi !' println('meow') } fn (c Cat) name_detailed(pet_name string) string { return '${pet_name} the ${typeof(c).name}, breed:${c.breed}' } fn (mut c Cat) set_breed(new string) { c.breed = new } // utility function to override default conversion to string, as a sample fn (c Cat) str() string { return 'Custom string conversion for Cat: ${c.breed}' } fn (d Dog) speak(s string) { assert s == 'Hi !' println('woof') } fn (d Dog) name() string { assert d.breed == 'Labrador Retriever' return 'Dog' } fn (d Dog) name_detailed(pet_name string) string { return '${pet_name} the ${typeof(d).name}, breed:${d.breed}' } fn (mut d Dog) set_breed(new string) { println('Nah') } // do not add to Dog the utility function 'str', so the default one will be used, as a sample struct Bird { mut: breed string } fn (b Bird) speak(s string) { println('tweet') } fn (b Bird) name() string { return b.breed } fn (b Bird) name_detailed(pet_name string) string { return '${pet_name} the ${typeof(b).name}, breed:${b.breed}' } fn (mut b Bird) set_breed(new string) { println('canary') b.breed = new } // do not add to Bird the utility function 'str', so the default one will be used, as a sample fn is_dog_or_cat(a Animal) bool { is_dog := a is Dog is_cat := a is Cat println('Animal is Dog or Cat: is a Dog: ${is_dog}, is a Cat: ${is_cat}') // return is_dog || is_cat // shorter syntax is_dog_or_cat := if a is Dog || a is Cat { true } else { false } println('Animal is Dog or Cat: ${is_dog_or_cat}') return is_dog_or_cat } fn is_dog_or_cat_or_bird(a Animal) bool { ret := a is Dog || a is Cat || a is Bird println('Animal is Dog or Cat or Bird: ${ret}') return ret } fn perform_speak(a Animal) { println('---- ${@FN}, given Animal: ${a} ----') a.speak('Hi !') assert true name := a.name() assert name == 'Dog' || name == 'Cat' if a is Dog { assert name == 'Dog' assert a.breed == 'Labrador Retriever' // test smart casting println(a.breed) } println(a.name()) println('Got animal of type: ${typeof(a).name}') // TODO: get implementation type (if possible) assert a is Dog || a is Cat assert is_dog_or_cat(a) } fn perform_speak_on_ptr(a &Animal) { println('---- ${@FN}, given &Animal: ${a} ----') a.speak('Hi !') assert true name := a.name() assert name == 'Dog' || name == 'Cat' if a is Dog { assert name == 'Dog' } println(a.name()) println('Got animal of type: ${typeof(a).name}') // TODO: get implementation type (if possible) assert a is Dog || a is Cat assert is_dog_or_cat(a) } fn test_perform_speak() { println('---- ${@FN} ----') dog := Dog{ breed: 'Labrador Retriever' } perform_speak(dog) perform_speak_on_ptr(dog) cat := Cat{ breed: 'Persian' } perform_speak(cat) perform_speak(Cat{ breed: 'Persian' }) perform_speak(new_cat('Persian')) perform_speak_on_ptr(cat) perform_speak_on_ptr(Cat{ breed: 'Persian' }) perform_speak_on_ptr(new_cat('Persian')) handle_animals([dog, cat]) } fn change_animal_breed(mut a Animal, new string) { a.set_breed(new) } fn test_interface_ptr_modification() { println('---- ${@FN} ----') mut cat := Cat{ breed: 'Persian' } change_animal_breed(mut cat, 'Siamese') assert cat.breed == 'Siamese' } fn perform_name_detailed(a Animal) { name_full := a.name_detailed('MyPet') println(name_full) assert name_full.starts_with('MyPet the Dog') || name_full.starts_with('MyPet the Cat') } fn test_perform_name_detailed() { println('---- ${@FN} ----') dog := Dog{ breed: 'Labrador Retriever' } println('Test on Dog: ${dog} ...') // using default conversion to string perform_name_detailed(dog) cat := Cat{} println('Test on empty Cat: ${cat} ...') perform_speak(cat) println('Test on a Persian Cat: ...') perform_speak(Cat{ breed: 'Persian' }) cat_persian2 := Cat{ breed: 'Persian' } println('Test on another Persian Cat: "${cat_persian2}" ...') perform_speak(cat_persian2) cat_persian2_str := cat_persian2.str() println("Persian Cat 2: '${cat_persian2_str}' ...") assert cat_persian2_str == 'Custom string conversion for Cat: Persian' println('Test (dummy/empty) on array of animals ...') handle_animals([dog, cat]) handle_animals_mutable([dog, cat]) assert true } fn handle_animals(a []Animal) { } fn handle_animals_mutable(a []Animal) { } interface Register { register() } struct RegTest { a int } fn (f RegTest) register() { } fn handle_reg(r Register) { r.register() } fn test_register() { println('---- ${@FN} ----') f := RegTest{} f.register() handle_reg(f) } interface Speaker2 { name() string speak() return_speaker() Speaker2 mut: return_speaker2() ?Speaker2 } struct Boss { mut: name string } fn (b Boss) name() string { return b.name } fn (b Boss) speak() { println("i'm ${b.name}") } fn (b &Boss) return_speaker() Speaker2 { return b } fn (mut b Boss) return_speaker2() ?Speaker2 { if b.name == 'richard' { return none } b.name = 'boss' return b } fn return_speaker2(mut sp Speaker2) Speaker2 { s := sp.return_speaker() s2 := sp.return_speaker2() or { return *sp } s.speak() s2.speak() return s2 } fn test_interface_returning_interface() { mut b := Boss{'bob'} assert b.name == 'bob' s2 := return_speaker2(mut b) if s2 is Boss { assert s2.name == 'boss' } } struct Foo { animal Animal animals []Animal } interface Animal { name() string name_detailed(pet_name string) string speak(s string) mut: set_breed(s string) } fn test_interface_array() { println('---- ${@FN} ----') println('Test on array of animals ...') mut animals := []Animal{} animals = [Cat{}, Dog{ breed: 'Labrador Retriever' }] assert true animals << Cat{} assert true animals << Bird{} assert true // println('Animals array contains: ${animals.str()}') // explicit call to 'str' function println('Animals array contains: ${animals}') // implicit call to 'str' function assert animals.len == 4 } fn test_interface_ptr_array() { println('---- ${@FN} ----') mut animals := []&Animal{} animals = [Cat{}, Dog{ breed: 'Labrador Retriever' }] assert true animals << Cat{} assert true animals << Bird{} assert true // println('Animals array contains: ${animals.str()}') // explicit call to 'str' function println('Animals array contains: ${animals}') // implicit call to 'str' function assert animals.len == 4 } fn test_is() { println('---- ${@FN} ----') dog := Dog{} assert is_dog_int(dog) == 1 } fn is_dog_int(a Animal) int { if a is Dog { return 1 } else { return 0 } } fn test_is_bool() { println('---- ${@FN} ----') dog := Dog{} assert is_dog(dog) == true assert is_dog_or_cat(dog) cat := Cat{} assert is_dog(cat) == false assert is_dog_or_cat(cat) bird := Bird{} assert is_dog(bird) == false assert !is_dog_or_cat(bird) assert is_dog_or_cat_or_bird(bird) } fn is_dog(a Animal) bool { println("Got animal: '${a}'") // implicit call to 'str' function of implementations println('with type: ${typeof(a).name}') // get implementation type (if possible) // sample (additional checks) here is_dog_or_cat := if a is Dog || a is Cat { true } else { false } println('Animal is Dog or Cat: ${is_dog_or_cat}') // use/test even another syntax if a is Dog { return true } else { return false } } fn new_animal() Animal { dog := Dog{} return dog } fn new_animal2() Animal { return new_animal() } /* // TODO fn animal_match(a Animal) { match a { Dog { println('(dog)') } Cat { println('(cat)') } else { println('(other)' } } } */ interface II { mut: my_field int } struct AA { BB } struct BB { pad [10]u8 mut: my_field int } // the opposite example of interface_mutability_receiver.vv // related to https://github.com/vlang/v/issues/1081 and https://github.com/vlang/v/issues/7338 // test example code by https://github.com/nedpals copied and adapted from https://github.com/vlang/v/issues/7338 // we accept immutable get_name even if the interface specified `mut` as it is consistent with function param behavior struct Dog2 { pub mut: name string } fn (d Dog2) get_name() string { return d.name } // mut get_name might be a bad example // we try to show that an interface can be more liberal in mut than the implementor, // while our error check is for the opposite case interface Animal2 { mut: get_name() string } fn get_animal_name(mut a Animal2) string { return a.get_name() } fn test_aa() { println('---- ${@FN} ----') mut aa := AA{} mut ii := II(aa) assert ii.my_field == 0 aa.my_field = 123 assert ii.my_field == 123 ii.my_field = 1234 assert aa.my_field == 1234 mut dog := Dog2{'Doggo'} println(dog.name) println(get_animal_name(mut dog)) } type Text = string fn (t Text) display() string { return t } interface Displayable { display() string } fn print_displayable(ds ...Displayable) { for d in ds { println(d.display()) } } fn test_variadic_interface() { print_displayable(Text('test'), Text('hehe')) } interface MyError { print_error() } struct CustomError implements MyError { } fn (e CustomError) print_error() { } fn handle_implements(e MyError) { e.print_error() } fn test_implements() { e := CustomError{} handle_implements(e) println('implements ok') } interface VtableDrawable { draw() int } interface VtableCalculator { add(a int, b int) int multiply(x int) int } struct VtablePoint { x int y int } fn (p VtablePoint) draw() int { return p.x * 1000 + p.y } fn (p VtablePoint) add(a int, b int) int { return p.x + p.y + a + b } fn (p VtablePoint) multiply(x int) int { return (p.x + p.y) * x } fn test_interface_value_receiver_dispatch() { p1 := VtablePoint{ x: 7 y: 3 } d1 := VtableDrawable(p1) assert d1.draw() == 7003 p2 := VtablePoint{ x: 15 y: 25 } d2 := VtableDrawable(p2) assert d2.draw() == 15025 p3 := VtablePoint{ x: 1 y: 1 } d3 := VtableDrawable(p3) assert d3.draw() + d3.draw() == 2002 } fn test_interface_value_receiver_dispatch_with_params() { calc := VtableCalculator(VtablePoint{ x: 10 y: 5 }) assert calc.add(3, 7) == 25 assert calc.multiply(4) == 60 calc2 := VtableCalculator(VtablePoint{ x: 2 y: 3 }) assert calc2.add(1, 1) == 7 assert calc2.multiply(10) == 50 }