如何创建异构的对象集合?

时间:2015-01-15 05:03:01

标签: rust

我想在Vec中使用特征对象。在C ++中,我可以创建一个基类Thing,从中派生Monster1Monster2。然后我可以创建一个std::vector<Thing*>Thing个对象必须存储一些数据,例如x : int, y : int,但派生类需要添加更多数据。

目前我有类似

的东西
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,
}

我使用ThingTraitget_thing_record()attack()等方法定义make_noise(),并为Monster1Monster2实施这些方法。< / p>

1 个答案:

答案 0 :(得分:17)

特质对象

实现对象的异构集合(在本例中为向量)的最可扩展方法正是您所拥有的:

Vec<Box<dyn ThingTrait + 'static>>

虽然有些时候你可能想要一个不是'static的生命,所以你需要这样的东西:

Vec<Box<dyn ThingTrait + 'a>>

您还可以将引用集合到特征中,而不是盒装特征:

Vec<&dyn ThingTrait>

一个例子:

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等都是特质对象。这些允许在无数种类型上实现特征,但权衡是需要一定量的间接和动态调度。

另见:

枚举

如评论中所述,如果您有固定数量的已知替代方案,则开放性较低的解决方案是使用枚举。这不要求值为Box,但它仍然会有少量的动态调度来决定运行时存在哪个具体的枚举变体:

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