rust

rust - ¿Cómo creo una colección heterogénea de objetos?



(1)

Rasgos de objetos

La forma más extensible de implementar una colección heterogénea (en este caso, un vector) de objetos es exactamente lo que tiene:

Vec<Box<dyn ThingTrait + ''static>>

Aunque hay momentos en los que podría desear una vida que no sea ''static , entonces necesitaría algo como:

Vec<Box<dyn ThingTrait + ''a>>

También podría tener una colección de referencias a rasgos, en lugar de rasgos encuadrados:

Vec<&dyn ThingTrait>

Un ejemplo:

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<dyn ThingTrait>> = vec![Box::new(m1), Box::new(m2)]; }

Box<SomeTrait> , Rc<SomeTrait> , &SomeTrait , etc. son todos objetos de rasgo . Estos permiten la implementación del rasgo en un número infinito de tipos, pero la desventaja es que requiere cierta cantidad de indirección y despacho dinámico.

Ver también:

  • ¿Qué hace que algo sea un "objeto de rasgo"?
  • ¿Qué significa "dyn" en un tipo?

Enumeraciones

Como se menciona en los comentarios, si tiene un número fijo de alternativas conocidas, una solución menos abierta es usar una enumeración. Esto no requiere que los valores sean Box ed, pero aún tendrá una pequeña cantidad de despacho dinámico para decidir qué variante de enumeración concreta está presente en el tiempo de ejecución:

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

Quiero usar objetos de rasgos en una Vec . En C ++ podría hacer una Thing clase base de la que se deriva Monster1 y Monster2 . Entonces podría crear un std::vector<Thing*> . Thing objetos deben almacenar algunos datos, por ejemplo, x : int, y : int , pero las clases derivadas deben agregar más datos.

Actualmente tengo algo como

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, }

ThingTrait un ThingTrait con métodos para get_thing_record() , attack() , make_noise() etc. y los implemento para Monster1 y Monster2 .