Trait is like virtual-class in C++, or interface in lots of other languages.
trait Animal {
fn sound(&self) -> String;
}
struct Cat {}
impl Animal for Cat {
fn sound(&self) -> String {
"Meow Meow".to_owned()
}
}
fn main() {
let cat = Cat {};
println!("Cat {}", cat.sound());
}
Run it:
Cat Meow Meow
We can use impl Trait
as the return type:
...
fn adopt_cat() -> impl Animal {
Cat {}
}
fn main() {
let cat = adopt_cat();
println!("Cat {}", cat.sound());
}
If we have 2 types of animal here, can we return different types with impl Animal
? let's try it:
trait Animal {
fn sound(&self) -> String;
}
struct Cat {}
impl Animal for Cat {
fn sound(&self) -> String {
"Meow Meow".to_owned()
}
}
struct Dog {}
impl Animal for Dog {
fn sound(&self) -> String {
"Bark Bark".to_owned()
}
}
fn adopt_animal(animal: &str) -> impl Animal {
if animal == "cat" {
Cat {}
} else {
Dog {}
}
}
fn main() {
let animal = adopt_animal("cat");
println!("Animal {}", animal.sound());
}
Here we defined Cat and Dog structs, and implemented the "Animal" trait for them, and in the adopt_animal
function, our return type is impl Animal
, because we know both Cat and Dog implemented the Animal
trait.
Let's run it:
|
22 | / if animal == "cat" {
23 | | Cat {}
| | ------ expected because of this
24 | | } else {
25 | | Dog {}
| | ^^^^^^ expected struct `Cat`, found struct `Dog`
26 | | }
| |_____- if and else have incompatible types
|
Ah, we got an error here. Turns out although both Cat and Dog implemented the Animal trait, it will find the first return type and treat that as the return type. In our case, Rust treats Cat
object as the returned type, so later it finds we might also return Dog
, it reports an error.
This is not really utilizing generics. If we want to return either type, we need to change the return type to Box<dyn Animal>
:
...
fn adopt_animal(animal: &str) -> Box<dyn Animal> {
if animal == "cat" {
Box::new(Cat {})
} else {
Box::new(Dog {})
}
}
fn main() {
let animal = adopt_animal("cat");
println!("Animal {}", animal.sound());
let animal = adopt_animal("dog");
println!("Animal {}", animal.sound());
}
Now we are able to run it:
Animal Meow Meow
Animal Bark Bark
P.S. We can also use trait to implement the "Template Method" design pattern, I wrote an article here before.
Top comments (0)