Reputation: 15365
Started learning rust this weekend, and I'm trying to write a little private helper method on my class to look up some internal values, but it's proving quite complicated. I have a few structs implementing a trait:
trait Animal {}
struct Dog {}
impl Dog for Animal{}
struct Cat{}
impl Cat for Animal{}
along with a container which holds multiple variants of animal:
struct Person {
my_dog: Dog,
my_cat: Cat
}
What I want to do is write a helper which looks up the appropriate Animal, like this:
impl Person {
fn look_up_animal(&self, animal_name: &str) -> Box<&dyn Animal> {
match animal_name {
"dog" => Box::from(&self.my_dog),
"cat" => Box::from(&self.my_cat)
}
}
}
Unfortunately, I can't quite figure out how to do the types for this. The version above using references and Box
gives an error like "expected struct Box<&dyn Animal>
but got struct Box<&Dog>
, so I'm not really sure how to convert a regular value into a dyn
value.
Upvotes: 6
Views: 3618
Reputation: 10434
The fully explicit version of what you want would be
impl Person {
fn look_up_animal(&self, animal_name: &str) -> Option<Box<&dyn Animal>> {
match animal_name {
"dog" => Some(Box::from(&self.my_dog as &dyn Animal)),
"cat" => Some(Box::from(&self.my_cat as &dyn Animal)),
_ => None,
}
}
}
Rust is able to coerce one layer of references to trait objects, but two layers of references (both Box
and &
) trips it up. (Having a reference inside a Box
also triggers a clippy
warning.) That suggests another solution.
impl Person {
fn look_up_animal(&self, animal_name: &str) -> Option<&dyn Animal> {
match animal_name {
"dog" => Some(&self.my_dog as &dyn Animal),
"cat" => Some(&self.my_cat as &dyn Animal),
_ => None,
}
}
}
This time, the explicit cast as &dyn Animal
is unnecessary since there's just a single layer of references.
impl Person {
fn look_up_animal(&self, animal_name: &str) -> Option<&dyn Animal> {
match animal_name {
"dog" => Some(&self.my_dog),
"cat" => Some(&self.my_cat),
_ => None,
}
}
}
Note: I added in the Option
so that there's something to return when animal_name
isn't "dog"
or "cat"
. You could also panic or default to something else that implements Animal
.
Upvotes: 13