Implement Foo trait for &dyn Foo

Let me show my problem with the standard Animal example:


pub trait Animal {
    fn age(&self, system: &Coordinates) -> i32;
}

pub struct Cat{ pub age: i32 }

impl Animal for Cat {
    fn age(&self, system: &Coordinates) -> i32 { self.age }
}

pub struct Zoo {
    pub zoo: Vec<Box<dyn Animal>>
}

impl Zoo {
    pub fn add_animal(&self, a: &dyn Animal) {
        self.zoo.push(Box::new(a));
    }
}

Compiler complaints about:
The trait `Animal` is not implemented for `&dyn Animal
I've learned in the docs that &dyn Animal and Animal are different types. But I don't know how to make it working. Can I implement:

impl Animal for &dyn Animal {}

and make just delegations ? This doesn't sound right to me ... Or do I have to implement &dyn Animal for Cat and all other animals I'll have in my code? The last option would be just silly copy&paste of the very same code.

So I've tried to impl Animal for &dyn Animal {} and now compiler complains this data with an anonymous lifetime `'_` for add_animal().

I'm sure this is a very basic problem, so what is the rust-onic way to create such a structure?

Why not take a Box<dyn Animal> in the function add_animal?

playground:

// zoo: Vec<Box<dyn Animal>>
pub fn add_animal(&mut self, a: Box<dyn Animal>)

// Or: zoo: Vec<&'animal dyn Animal>
pub fn add_animal(&mut self, a: &'animal dyn Animal)

Either make add_animal accept Box<dyn Animal> or you’ll need a way to clone animals; otherwise you’ll be trying to insert a short-lived borrow of an animal (&dyn Animal) into an owning, lifetime-less collection (Zoo).

Technically, adding a lifetime argument Zoo<'a> to the struct (and implementing Animal for &dyn Animal) could work, too, but it may easily run into problems down the line when using such a struct, so it’s often not the most useful approach.

If you need to duplicate animals, you can add a method to the trait, e.g.

pub trait Animal {
    fn age(&self, system: &Coordinates) -> i32;
    fn dyn_clone(&self) -> Box<dyn Animal>; // IDK the best name for this method
}

(and implement it accordingly).

The implementation side of the trait can be simplified (so that dyn_clone doesn’t need to be implemented manually) with a supertrait and a blanked impl, e.g.

pub struct Coordinates;

pub trait CloneAnimal {
    fn dyn_clone(&self) -> Box<dyn Animal>;
}

impl<T: Animal + Clone + 'static> CloneAnimal for T {
    fn dyn_clone(&self) -> Box<dyn Animal> {
        Box::new(self.clone())
    }
}

pub trait Animal: CloneAnimal {
    fn age(&self, system: &Coordinates) -> i32;
}

#[derive(Clone)]
pub struct Cat{ pub age: i32 }

impl Animal for Cat {
    fn age(&self, system: &Coordinates) -> i32 { self.age }
}

pub struct Zoo {
    pub zoo: Vec<Box<dyn Animal>>
}

impl Zoo {
    pub fn add_animal(&mut self, a: &dyn Animal) {
        self.zoo.push(a.dyn_clone());
    }
}

Alternatively, in certain situations, a crate like dyn-clone could be useful or convenient, too.


In any case, even with “clonable” dyn Animal, it might still make sense to accept Box<dyn Animal> in the add_animal method, and make the caller do the cloning if (and only if) that’s necessary.

1 Like

You can also make the add method generic, instead of taking any kind of dyn Animal:

impl Zoo {
    pub fn add_animal<A:Animal>(&self, a: A) {
        self.zoo.push(Box::new(a));
    }
}

or

impl Zoo {
    pub fn add_animal<A:Animal+Clone>(&self, a: &A) {
        self.zoo.push(Box::new(a.clone()));
    }
}

Downside: If you already have a Box<dyn Animal> to begin with, this would add another layer of indirection and another level of dynamic abstraction without need. And if you already have &dyn Animal neither of those two versions can accept it. It all depends on whether or not encountering dyn Animal is common or rare; e.g. whether not there’s important/common API that returns Box<dyn Animal> or &dyn Animal.

1 Like

Thanks a lot for answers!

I decided to change my add_animal() method so it accepts Box<dyn Animal>. Now it compiles, but I'd like to clarify:

Has the ownership of my Animal object got moved to the Zoo? E.g. I can't access it in the main() any longer once add_animal() was called?

Yes indeed Box<Cat> owns the cat inside, and Box<dyn Animal> created from it still owns it. So ownership is moved by moving/passing the Box<dyn Animal>.

If you need to access it after putting it into the Zoo, you might be able to get a borrow of the animal through some method in your Zoo. There's also the option of shared ownership, if the Zoo shouldn't exclusively own the animals (sometimes useful when the way you try to work with & creates too many lifetime problems) which would be to use Arc (or Rc), e. g. in place of the Box, so you could habe Arc<dyn Animal> in the Zoo if shared ownership is the common case. Or you add a blanket impl Animal for Arc<T> where T: Animal, and then you could turn Box<Arc<Cat>> into Box<dyn Animal>, too (in case such shared ownership is not too common, so the default with Box makes sense; and this probably also requires Animal to have only &self methods, so you can write this blanket impl).

Given the various recommendations, I'd suggest a generic, then either pass in an owned value (not a ref), an Rc, an Arc or a Cow<'lifetime, dyn Animal>. Could also just have what you have with a lifetime specifier.
Of course ALL calls in and out need to be the same with the generic. I'm liking Cow recently because it happens to fit my use cases nicely and avoids needing two generics (one for ownership and one for reference) - can even mix the two in the same vector.

With the cow you could have two add funcs - owning and referenced (but the ref lifetime will be the killer choke point)

Cow is just awesome for animals too :sunglasses:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.