Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to clone a struct storing a boxed trait object?

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?

like image 389
Denis Kreshikhin Avatar asked May 20 '15 15:05

Denis Kreshikhin


Video Answer


1 Answers

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.

like image 196
DK. Avatar answered Sep 29 '22 12:09

DK.