I wrote a program that has the trait Animal
and the struct Dog
implementing the trait. It also has a struct AnimalHouse
storing an animal as a trait object Box<Animal>
.
trait Animal { fn speak(&self); } struct Dog { name: String, } impl Dog { fn new(name: &str) -> Dog { return Dog { name: name.to_string(), }; } } impl Animal for Dog { fn speak(&self) { println!{"{}: ruff, ruff!", self.name}; } } struct AnimalHouse { animal: Box<Animal>, } fn main() { let house = AnimalHouse { animal: Box::new(Dog::new("Bobby")), }; house.animal.speak(); }
It returns "Bobby: ruff, ruff!" as expected, but if I try to clone house
the compiler returns errors:
fn main() { let house = AnimalHouse { animal: Box::new(Dog::new("Bobby")), }; let house2 = house.clone(); house2.animal.speak(); }
error[E0599]: no method named `clone` found for type `AnimalHouse` in the current scope --> src/main.rs:31:24 | 23 | struct AnimalHouse { | ------------------ method `clone` not found for this ... 31 | let house2 = house.clone(); | ^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: candidate #1: `std::clone::Clone`
I tried to add #[derive(Clone)]
before struct AnimalHouse
and got another error:
error[E0277]: the trait bound `Animal: std::clone::Clone` is not satisfied --> src/main.rs:25:5 | 25 | animal: Box<Animal>, | ^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `Animal` | = note: required because of the requirements on the impl of `std::clone::Clone` for `std::boxed::Box<Animal>` = note: required by `std::clone::Clone::clone`
How do I make the struct AnimalHouse
cloneable? Is it idiomatic Rust to use a trait object actively, in general?
There are a few problems. The first is that there's nothing to require that an Animal
also implements Clone
. You could fix this by changing the trait definition:
trait Animal: Clone { /* ... */ }
This would cause Animal
to no longer be object safe, meaning that Box<dyn Animal>
will become invalid, so that's not great.
What you can do is insert an additional step. To whit (with additions from @ChrisMorgan's comment).
trait Animal: AnimalClone { fn speak(&self); } // Splitting AnimalClone into its own trait allows us to provide a blanket // implementation for all compatible types, without having to implement the // rest of Animal. In this case, we implement it for all types that have // 'static lifetime (*i.e.* they don't contain non-'static pointers), and // implement both Animal and Clone. Don't ask me how the compiler resolves // implementing AnimalClone for dyn Animal when Animal requires AnimalClone; // I have *no* idea why this works. trait AnimalClone { fn clone_box(&self) -> Box<dyn Animal>; } impl<T> AnimalClone for T where T: 'static + Animal + Clone, { fn clone_box(&self) -> Box<dyn Animal> { Box::new(self.clone()) } } // We can now implement Clone manually by forwarding to clone_box. impl Clone for Box<dyn Animal> { fn clone(&self) -> Box<dyn Animal> { self.clone_box() } } #[derive(Clone)] struct Dog { name: String, } impl Dog { fn new(name: &str) -> Dog { Dog { name: name.to_string(), } } } impl Animal for Dog { fn speak(&self) { println!("{}: ruff, ruff!", self.name); } } #[derive(Clone)] struct AnimalHouse { animal: Box<dyn Animal>, } fn main() { let house = AnimalHouse { animal: Box::new(Dog::new("Bobby")), }; let house2 = house.clone(); house2.animal.speak(); }
By introducing clone_box
, we can get around the problems with attempting to clone a trait object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With