Traits aren't types. But if a trait is dyn
-safe, aka object-safe, you can coerce a variable with a type that implements the trait into a dyn Trait
(aka a trait object). A dyn Trait
is a concrete type. You may want to read this recent introduction.
In addition to that introduction, given your example, I'll also note that the only subtying in Rust is related to lifetimes. In particular, a type that implements a trait is not a subtype of the corresponding dyn Trait
, even though it can be coerced into a dyn Trait
.
Let's start by cleaning your main
function a bit.
let args: Vec<String> = env::args().collect();
let denis = Human::new("Denis".to_owned(), 16);
let chip = Dog::new("Chip".to_owned(), 4);
- let animal: Animal;
+ // traits aren't types, and `dyn Trait` needs to be behind a pointer
+ // of some kind. Since this one is owned, let's use a `Box`.
+ let animal: Box<dyn Animal>;
if args.len() > 1 && args[1] == "human" {
- animal = denis.to_owned() as Animal;
+ // Unsized coercion to `dyn Animal` will take place automatically
+ // here as we pass the value to `Box::new`.
+ animal = Box::new(denis);
} else {
- animal = chip.to_owned() as Animal;
+ // Same idea
+ animal = Box::new(chip);
}
print_animal_name_gen(&animal);
- print_animal_name_dyn(&dyn animal);
+ // `dyn` isn't an operation -- you want to deref the Box
+ // to get to the `dyn Animal` within
+ print_animal_name_dyn(&*animal);
We're closer now, but like that other thread said,
- You have to implement
Trait for Box<dyn Trait>
yourself if you want it.
- Which is why passing a
&Box<dyn Animal>
isn't working.
-
Sized
is implied most places and dyn Trait
isn't Sized
.
The fix for the latter bullet point is
-fn print_animal_name_gen<T>(a: &T) where T: Animal,
+fn print_animal_name_gen<T>(a: &T) where T: Animal + ?Sized,
And to implement the trait for Box<dyn Animal>
, you can do:
impl Animal for Box<dyn Animal + '_> {
/*
fn get_name(&self) -> String {
(**self).get_name()
}
*/
fn get_age(&self) -> i32 {
(**self).get_age()
}
}
Playground with both.
I left the implementation of get_name
commented out as it's a relevant point -- if you don't override the default implementation, you'll get the default implementation, even for Box<dyn Trait>
. You generally want to override any default functions for your Box<dyn Trait>
implementations, etc.
If you uncomment the get_name
implementation, you'll see it works more like you presumably expected.