2014-09-12 14 views
36

Rozważmy następujący kod:wektorowych obiektów należących do danej cechy

trait Animal { 
    fn make_sound(&self) -> String; 
} 

struct Cat; 
impl Animal for Cat { 
    fn make_sound(&self) -> String { 
     "meow".to_string() 
    } 
} 

struct Dog; 
impl Animal for Dog { 
    fn make_sound(&self) -> String { 
     "woof".to_string() 
    } 
} 

fn main() { 
    let dog: Dog = Dog; 
    let cat: Cat = Cat; 
    let v: Vec<Animal> = Vec::new(); 
    v.push(cat); 
    v.push(dog); 
    for animal in v.iter() { 
     println!("{}", animal.make_sound()); 
    } 
} 

Kompilator mówi mi, że v jest wektorem Animal gdy próbuję naciskać cat (typ niedopasowanie)

Tak, jak czy mogę stworzyć wektor obiektów należących do cechy i wywołać odpowiednią metodę cech na każdym elemencie?

Odpowiedz

48

Vec<Animal> nie jest legalny, ale kompilator nie może ci tego powiedzieć, ponieważ niedopasowanie typu w jakiś sposób go ukrywa. Jeśli usuwamy połączeń do push, kompilator daje nam następujący błąd:

<anon>:22:9: 22:40 error: instantiating a type parameter with an incompatible type `Animal`, which does not fulfill `Sized` [E0144] 
<anon>:22  let mut v: Vec<Animal> = Vec::new(); 
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

Powodem, że nie jest to legalne, że Vec<T> przechowuje wiele T obiekty kolejno w pamięci. Jednakże, Animal jest cechą, a cechy nie mają rozmiaru (Cat i Dog nie mają gwarantowanego tego samego rozmiaru).

Aby rozwiązać ten problem, musimy przechowywać coś, co ma rozmiar w Vec. Najprostszym rozwiązaniem jest zawijanie wartości w Box, tj. Vec<Box<Animal>>. Box<T> ma ustalony rozmiar ("gruby wskaźnik", jeśli T jest cechą, w przeciwnym razie prostym wskaźnikiem).

Oto pracuje main:

fn main() { 
    let dog: Dog = Dog; 
    let cat: Cat = Cat; 
    let mut v: Vec<Box<Animal>> = Vec::new(); 
    v.push(Box::new(cat)); 
    v.push(Box::new(dog)); 
    for animal in v.iter() { 
     println!("{}", animal.make_sound()); 
    } 
} 
+0

Ah widzę. Ma sens, że wektory wymagają deterministycznych typów wielkości. Dzięki! –

7

Można użyć odniesienia cecha obiektu &Animal pożyczyć elementy i przechowywać te cechy obiektów w Vec. Następnie możesz ją wyliczyć i użyć interfejsu cechy.

zmiany Vec „s typu rodzajowego dodając & przed cecha będzie działać:

fn main() { 
    let dog: Dog = Dog; 
    let cat: Cat = Cat; 
    let mut v: Vec<&Animal> = Vec::new(); 
    //    ~~~~~~~ 
    v.push(&dog); 
    v.push(&cat); 
    for animal in v.iter() { 
     println!("{}", animal.make_sound()); 
    } 
    // Ownership is still bound to the original variable. 
    println!("{}", cat.make_sound()); 
} 

To jest wielki jeśli możesz zmienną oryginalną zachować własność i używać go później.

Należy pamiętać, że zgodnie z powyższym scenariuszem nie można przenieść własności na dog ani cat, ponieważ Vec pożyczył te konkretne wystąpienia w tym samym zakresie.

Wprowadzenie nowego zakresu może pomóc obsłużyć tej konkretnej sytuacji:

fn main() { 
    let dog: Dog = Dog; 
    let cat: Cat = Cat; 
    { 
     let mut v: Vec<&Animal> = Vec::new(); 
     v.push(&dog); 
     v.push(&cat); 
     for animal in v.iter() { 
      println!("{}", animal.make_sound()); 
     } 
    } 
    let pete_dog: Dog = dog; 
    println!("{}", pete_dog.make_sound()); 
}