2015-05-20 3 views
5

Я написал программу, которая имеет trait Animal и struct Dog, реализуя черту и struct AnimalHouse хранение животного как объекта объекта 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(); 
} 

Он отлично работает и возвращает «Бобби: ерш, ерш!». как и ожидалось.

Но если я пытаюсь клонировать house компилятор возвращает ошибку

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 

Я пытался добавить #[derive(Clone)] до того struct AnimalHouse и получил еще ошибки:

 
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 

Так как сделать-структуру Animal House cloneable? Это обычный способ для ржавчины активно использовать объект-признак (в общем)?

ответ

10

Есть несколько проблем. Во-первых, нет необходимости требовать, чтобы Animal также реализовывал Clone. Теперь вы можете это исправить, изменив определение признака:

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

Но это вызывает Animal больше не будет возражать безопасным, а это означает, что Box<Animal> станет недействительным. Так что это не здорово.

Что вы может сделать дополнительный шаг. To whit:

Редактировать: Следующее изменение было изменено на основании комментария @ 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 { 
     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(); 
} 

Вводя clone_box, мы можем обойти проблемы с попыткой клонировать объект признака.

+0

Помещение 'clone_box' как метода по самому признаку довольно неэффективно, требуя, чтобы все реализаторы реализовали его таким же образом. Лучшее решение состоит в том, чтобы иметь это как супервирт «Животного» с реалистичной реализацией для «T: Animal + Clone». * Это * - это подход, используемый в таких вещах, как AnyMap. –

+0

@ChrisMorgan: Хорошая идея; изменилось. –

Смежные вопросы