2014-09-12 1 views
36

den folgenden Code vor:Vektor von Objekten zu einem Merkmal gehör

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

Der Compiler sagt mir, dass v ist ein Vektor von Animal wenn ich versuche, cat zu schieben (Typ Mismatch)

So, wie Kann ich einen Vektor von Objekten erstellen, die zu einem Merkmal gehören, und die entsprechende Trait-Methode für jedes Element aufrufen?

Antwort

48

Vec<Animal> ist nicht legal, aber der Compiler kann Ihnen das nicht sagen, weil der Typ stimmt nicht überein. Wenn wir die Anrufe push entfernen, wird der Compiler gibt uns die folgende Fehlermeldung:

<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(); 
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

Der Grund, warum das nicht legal ist, dass ein Vec<T> speichert viele T Objekte nacheinander im Speicher. Animal ist jedoch ein Merkmal, und Merkmale haben keine Größe (ein Cat und ein Dog sind nicht garantiert, um die gleiche Größe zu haben).

Um dieses Problem zu lösen, müssen wir etwas speichern, das eine Größe in der Vec hat. Die einfachste Lösung besteht darin, die Werte in eine Box, d. H. Vec<Box<Animal>>, zu verpacken. Box<T> hat eine feste Größe (ein "dicker Zeiger", wenn T ein Merkmal ist, ansonsten ein einfacher Zeiger).

Hier ist ein Arbeits 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 ich sehe. Es ist sinnvoll, dass Vektoren deterministisch große Typen benötigen. Vielen Dank! –

7

Sie &Animal ein Referenzmerkmal-Objekt verwenden, können die Elemente zu leihen und speichern diese Eigenschaft in einem Vec Objekte. Sie können es dann aufzählen und die Merkmalsschnittstelle verwenden.

die Vec Ändern ‚s generische Art durch eine & vor dem Merkmal Hinzufügen funktioniert:

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

Das ist großartig, wenn Sie die ursprüngliche Variable möchten Besitz halten und später wiederverwenden.

Beachten Sie bei dem oben genannten Szenario, dass Sie die Eigentumsrechte dog oder cat nicht übertragen können, da die Vec diese konkreten Instanzen im gleichen Bereich ausgeliehen hat.

einen neuen Bereich Einführung kann dazu beitragen, dass die besondere Situation umgehen:

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