简体   繁体   中英

Returning a reference to a Box<dyn Trait>

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.

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,
        }
    }
}

(playground link)


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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM