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
.