2015-05-20 5 views
5

Ich schrieb ein Programm, das trait Animal und struct Dog die Umsetzung der Eigenschaft und struct AnimalHouse Speichern eines Tieres als Merkmal Objekt Box<Animal> hat.Wie klonst du eine Struktur, die ein Objekt speichert?

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(); 
} 

Es funktioniert perfekt und gibt "Bobby: Halskrause, Halskrause!" wie erwartet.

Aber wenn ich versuche, house zu klonen der Compiler gibt Fehler

fn main(){ 
    let house = AnimalHouse{animal: Box::new(Dog::new("Bobby"))}; 
    let house2 = house.clone() 
    house2.animal.speak(); 
} 
 
32:31 error: type `AnimalHouse` does not implement any method in scope named `clone` 
    let house2 = house.clone(); 
         ^~~~~~~ 
32:31 help: methods from traits can only be called if the trait is implemented and in scope; the following trait defines a method `clone`, perhaps you need to implement it: 
32:31 help: candidate #1: `core::clone::Clone` 
error: aborting due to previous error 

Ich versuchte #[derive(Clone)] vor struct AnimalHouse hinzufügen und bekam einen anderen Fehler:

 
24:24 error: the trait `core::marker::Sized` is not implemented for the type `Animal` [E0277] 
    animal: Box 
        ^~~~~~~~~~~~~~~~~~~ 
22:15 note: in expansion of #[derive_Clone] 
22:15 note: expansion site 
24:24 note: `Animal` does not have a constant size known at compile-time 
    animal: Box 
        ^~~~~~~~~~~~~~~~~~~ 
22:15 note: in expansion of #[derive_Clone] 
22:15 note: expansion site 
24:24 error: the trait `core::clone::Clone` is not implemented for the type `Animal` [E0277] 
    animal: Box 
        ^~~~~~~~~~~~~~~~~~~ 
22:15 note: in expansion of #[derive_Clone] 
22:15 note: expansion site 
error: aborting due to 2 previous errors 

So wie die Struktur zu machen Animal House klonierbar? Ist es für Rost normal, ein Merkmalsobjekt aktiv (im Allgemeinen) zu benutzen?

Antwort

10

Es gibt ein paar Probleme. Die erste ist, dass es nichts zu erfordern, dass ein Animal implementiert auch Clone. Nun könnte man dieses Problem beheben, indem Sie die Eigenschaft Definition zu ändern:

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

Aber das verursacht Animal nicht mehr sicher sein, widersprechen, was bedeutet, dass Box<Animal> ungültig werden. Das ist nicht großartig.

Was Sie können tun, ist ein zusätzlicher Schritt einfügen. To Whit:

Bearbeiten: Das folgende wurde basierend auf @ ChrisMorgans Kommentar geändert.

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 { 
     return 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(); 
} 

Durch clone_box Einführung können wir um die Probleme zu bekommen mit dem Versuch, ein Merkmal Objekt zu klonen.

+0

Die Verwendung von 'clone_box' als Methode für das Merkmal selbst ist ziemlich ineffizient und erfordert von allen Implementierern, dass es auf die gleiche Weise implementiert wird. Eine bessere Lösung ist es, dies als ein "Tier" -Supertrait mit einer pauschalen Implementierung für "T: Animal + Clone" zu haben. * Dies ist der Ansatz, der in Dingen wie AnyMap verwendet wird. –

+0

@ChrisMorgan: Gute Idee; geändert. –

Verwandte Themen