¿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 deAnimal
, que requiere que cada implementación de animales sea clonable. -
Una línea para generar una implementación de la biblioteca estándar
Clone
paraBox<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();
}