2015-01-15 15 views
6

Desidero utilizzare gli oggetti tratto in un Vec. In C++ potrei creare una classe base Thing da cui deriva Monster1 e Monster2. Potrei quindi creare un std::vector<Thing*>. Gli oggetti Thing devono memorizzare alcuni dati, ad es. x : int, y : int, ma le classi derivate devono aggiungere più dati.Come posso creare una collezione eterogenea di oggetti?

Attualmente ho qualcosa di simile

struct Level { 
    // some stuff here 
    pub things: Vec<Box<ThingTrait + 'static>>, 
} 

struct ThingRecord { 
    x: i32, 
    y: i32, 
} 

struct Monster1 { 
    thing_record: ThingRecord, 
    num_arrows: i32, 
} 

struct Monster2 { 
    thing_record: ThingRecord, 
    num_fireballs: i32, 
} 

definisco un ThingTrait con i metodi per get_thing_record(), attack(), make_noise() ecc e la loro attuazione per Monster1 e Monster2.

+1

se i tuoi mostri sono per lo più noti in anticipo (cioè il vostro non è la creazione di un motore di gioco che permette a tutti di creare un nuovo mostro) si potrebbe in alternativa lavorare con un enum –

+5

Vedi anche [la discussione su reddit] (http://www.reddit.com/r/rust/comments/2sh28k/best_way_to_implement_heterogeneous_collection_of/). (A proposito, se si incrocia una domanda, è generalmente educato almeno il collegamento tra di loro in modo che, ad esempio, le persone interessate non perdano la discussione). – huon

+0

Ok. Sono combattuto tra l'utilizzo di Tratti e un metodo che restituisce i dati condivisi ... o semplicemente usando un enum per tutto. Penso che il primo sia il minore di due mali. Se l'ereditarietà verrà aggiunta alla lingua, come sarà? A parte questo, non mi manca quasi nulla di C++. Una boccata d'aria fresca. – stevenkucera

risposta

8

Il modo più estensibile per implementare una collezione eterogenea (in questo caso un vettore) di oggetti è esattamente quello che hai:

Vec<Box<ThingTrait + 'static>> 

Anche se ci sono momenti in cui si potrebbe desiderare una vita che non è 'static, così avresti bisogno di qualcosa come:

Vec<Box<ThingTrait + 'a>> 

si potrebbe anche avere una collezione di riferimenti a tratti, invece di tratti in scatola:

Vec<&ThingTrait> 

Un esempio:

trait ThingTrait { 
    fn attack(&self); 
} 

impl ThingTrait for Monster1 { 
    fn attack(&self) { 
     println!("monster 1 attacks") 
    } 
} 

impl ThingTrait for Monster2 { 
    fn attack(&self) { 
     println!("monster 2 attacks") 
    } 
} 

fn main() { 
    let m1 = Monster1 { 
     thing_record: ThingRecord { x: 42, y: 32 }, 
     num_arrows: 2, 
    }; 

    let m2 = Monster2 { 
     thing_record: ThingRecord { x: 42, y: 32 }, 
     num_fireballs: 65, 
    }; 

    let things: Vec<Box<ThingTrait>> = vec![Box::new(m1), Box::new(m2)]; 
} 

Come accennato nei commenti, una soluzione meno aperto è quello di utilizzare un enum. Questo non richiede che i valori siano Box ndr:

enum Monster { 
    One(Monster1), 
    Two(Monster2), 
} 

impl Monster { 
    fn attack(&self) { 
     match *self { 
      Monster::One(_) => println!("monster 1 attacks"), 
      Monster::Two(_) => println!("monster 2 attacks"), 
     } 
    } 
} 

fn main() { 
    let m1 = Monster1 { 
     thing_record: ThingRecord { x: 42, y: 32 }, 
     num_arrows: 2, 
    }; 

    let m2 = Monster2 { 
     thing_record: ThingRecord { x: 42, y: 32 }, 
     num_fireballs: 65, 
    }; 

    let things = vec![Monster::One(m1), Monster::Two(m2)]; 
} 
Problemi correlati