struct clone rust traits cloneable

¿Cómo clonar una estructura que almacena un objeto de rasgo en caja?



clone rust (3)

Hay algunos problemas La primera es que no hay nada que requiera que un Animal también implemente Clone . Puede solucionar esto cambiando la definición del rasgo:

trait Animal: Clone { /* ... */ }

Esto provocaría que Animal ya no sea seguro para objetos, lo que significa que Box<Animal> dejará de ser válido, por lo que no es genial.

Lo que puede hacer es insertar un paso adicional. Para whit (con adiciones del comentario de @ ChrisMorgan ).

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 Animal when Animal requires AnimalClone; I // have *no* idea why this works. trait AnimalClone { fn clone_box(&self) -> Box<Animal>; } impl<T> AnimalClone for T where T: ''static + Animal + Clone, { fn clone_box(&self) -> Box<Animal> { Box::new(self.clone()) } } // We can now implement Clone manually by forwarding to clone_box. impl Clone for Box<Animal> { fn clone(&self) -> Box<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<Animal>, } fn main() { let house = AnimalHouse { animal: Box::new(Dog::new("Bobby")), }; let house2 = house.clone(); house2.animal.speak(); }

Al presentar clone_box , podemos solucionar los problemas al intentar clonar un objeto de rasgo.

Escribí un programa que tiene el rasgo Animal y el Dog estructura que implementa el rasgo. También tiene una estructura AnimalHouse almacena un animal como un objeto de rasgo 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(); }

Devuelve "Bobby: ruff, ruff!" como se esperaba, pero si trato de clonar house el compilador devuelve errores:

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`

Intenté agregar #[derive(Clone)] antes de struct AnimalHouse y obtuve otro 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`

¿Cómo hago que la estructura AnimalHouse pueda clonar? ¿Es idiomático Rust usar activamente un objeto de rasgo, en general?


La respuesta anterior responde correctamente la pregunta sobre el almacenamiento de un objeto de rasgo en caja.

Saliendo del tema con respecto al título, pero no sobre la forma idiomática de usar objetos de rasgo, una solución alternativa podría ser usar el puntero inteligente Rc lugar de un Box : esto evita la solución para evitar la seguridad de los objetos:

#[derive(Clone)] struct AnimalHouse { animal: Rc<Animal>, } fn main() { let house = AnimalHouse { animal: Rc::new(Dog::new("Bobby")) }; let house2 = house.clone(); house2.animal.speak(); }

Nota : Rc<T> es solo para uso en escenarios de subproceso único; también hay Arc<T> .


Mi caja objekt implementa una versión reutilizable de la respuesta de DK . Con él, puede hacer que su código original funcione con un mínimo de cambios.

  • Una línea para importar la caja.
  • Una línea para agregar objekt::Clone como supertrait de Animal , que requiere que cada implementación de animales sea clonable.
  • Una línea para generar una implementación de la biblioteca estándar Clone para Box<Animal> .

#[macro_use] extern crate objekt; trait Animal: objekt::Clone { fn speak(&self); } clone_trait_object!(Animal); #[derive(Clone)] struct Dog { name: String, } impl Dog { fn new(name: &str) -> Dog { Dog { name: name.to_owned() } } } impl Animal for Dog { fn speak(&self) { println!{"{}: ruff, ruff!", self.name}; } } #[derive(Clone)] struct AnimalHouse { animal: Box<Animal>, } fn main() { let house = AnimalHouse { animal: Box::new(Dog::new("Bobby")), }; let house2 = house.clone(); house2.animal.speak(); }