如何将一个类型存储在一个数组中?

3

我想创建一个包含结构体类型(非实例化)的数组,它们都实现了相同的trait。我尝试过这样做:

trait TraitA {
    fn new(number: i16) -> Self;
    fn get_name() -> &'static str;
}

struct StructA {
    bar: u8
}
struct StructB {
    foo: i16
}

impl TraitA for StructA {
    fn new(number: i16) -> Self {
        StructA { bar: number as u8 }
    }
    fn get_name() -> &'static str
    { "StructA" }
}
impl TraitA for StructB {
    fn new(number: i16) -> Self {
        StructB { foo: number }
    }
    fn get_name() -> &'static str
    { "StructB" }
}

fn main() {
    let struct_array = [StructA, StructB];

    for i in 0..struct_array.len() {
        println!("{}", struct_array[i]::get_name());
        struct_array[i]::new(i);
    }
}

编译器期望的是实际值,而不是类型。
在Python中,我会这样做:
class ClassA:
    def __init__(self, number):
        self.bar = number
    def get_name():
        return "ClassA"
class ClassB:
    def __init__(self, number):
        self.foo = number
    def get_name():
        return "ClassB"

if __name__ == "__main__":
    class_array = [ClassA, ClassB]
    for i in range(2):    
        print(class_array[i].get_name())
        class_array[i](i)

我怎样才能实现这个?

不是类型,但也许你可以存储一个返回装箱特质对象的函数? - rodrigo
1
像这样的游乐场 - rodrigo
3
也许你应该考虑不使用静态函数,而是创建一个Factory特质或类,将所有的静态方法实现为非静态方法。然后只需保留一个Box<dyn Factory>向量即可。 - rodrigo
5
@op325所建议的方法非常简洁。在Rust中,类型不是一等公民——它们只存在于编译时,并且在运行时没有任何表示。如果你想要一个运行时表示,你需要自己定义构造器类型。如果你不需要在构造器上存储任何信息,这些类型可以是零大小的。 - Sven Marnach
1
Rust在运行时如何存储类型? - Shepmaster
显示剩余5条评论
1个回答

5
在Python中,类也是对象和可调用的,当你调用它们时,会创建一个新的该类型的对象。
在Rust中,正如SvenMarnach上面所评论的那样,类型不是对象;它们只存在于编译时,并且在运行程序中没有任何表示。
为了实现你想要的功能,你需要创建一个模仿Python类类型的类型。一种工厂模式,自然会成为特征。我假设你希望对这些TraitA对象进行某些操作,而不仅仅是创建它们,因此让我们添加一些有用的东西并将构建工作移动到另一个特征中:
trait TraitA {
    fn do_something(&self);
}
trait FactoryA {
    fn new(&self, number: i16) -> Box<dyn TraitA>;
    fn get_name(&self) -> &'static str;
}

请注意,我正在向每个函数添加 &self 参数。这是必需的,因为我们稍后将要调用动态分派,在 Rust 中,没有 self 就不能进行动态分派。但是这个 trait 代表了你的 Python 类,所以这些成员函数类似于 Python 的 类方法
此外,new 函数可以返回一个关联类型,但这也不适用于动态分派,所以我返回了一个 类型擦除Box<dyn TraitA>
现在,实现一对类相当无聊:
struct StructA {
    bar: u8
}
struct StructB {
    foo: i16
}

impl TraitA for StructA {
    fn do_something(&self) {
        println!("I'm an A({})", self.bar);
    }
}
impl TraitA for StructB {
    fn do_something(&self) {
        println!("I'm a B({})", self.foo);
    }
}

实现工厂模式更有趣:

struct BuilderA;

struct BuilderB;

impl FactoryA for BuilderA {
    fn new(&self, number: i16) -> Box<dyn TraitA> {
        Box::new(StructA { bar: number as u8 })
    }
    fn get_name(&self) -> &'static str
    { "StructA" }
}
impl FactoryA for BuilderB {
    fn new(&self, number: i16) -> Box<dyn TraitA> {
        Box::new(StructB { foo: number })
    }
    fn get_name(&self) -> &'static str
    { "StructB" }
}

我们在构建器中使用零大小的类型(ZST),因为我们没有需要存储的内容。
主函数也非常简单。我只是把您的循环改成了一个Iterator::enumerate,只是为了好玩。
fn main() {
    let struct_array: Vec<Box<dyn FactoryA>> = vec![
        Box::new(BuilderA),
        Box::new(BuilderB),
    ];

    for (i, b) in struct_array.iter().enumerate() {
        println!("{}", b.get_name());
        let a = b.new(i as i16);
        a.do_something();
    }
}

这段代码按预期运行并打印输出结果 (playground):
StructA
I'm an A(0)
StructB
I'm a B(1)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接