结构字段应该都具有相同的特征,但不一定是相同的类型

Struct fields should be all of same trait, but not neceesarily same type

我正在尝试实现以下特征和结构:

pub trait Funct {
    fn arity(&self) -> u32;
}

#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub struct FunctionLiteral<T: Funct> {
    pub function: T,
    pub args: Vec< FunctionLiteral<T> >
}

pub enum Foo {
    Foo
}

impl Funct for Foo {
    fn arity(&self) -> u32 {0}
}

pub enum Bar {
    Bar
}

impl Funct for Bar {
    fn arity(&self) -> u32 {0}
}

fn main() {
    let baz = FunctionLiteral{
        function: Foo::Foo,
        args: vec![FunctionLiteral{
            function: Bar::Bar, 
            args: vec![]
        }]
    };
}

我可以按照通用类型 T 的方式进行设置,使其具有特征 Funct,但我不一定想要 T 为同一类型。

这里,编译代码报如下错误:

error[E0308]: mismatched types
  --> foo.rs:31:23
   |
31 |             function: Bar::Bar, 
   |                       ^^^^^^^^ expected enum `Foo`, found enum `Bar`

error: aborting due to previous error

是否可以设置 FunctionLiteral 以便我可以为 functionargs 的项目设置不同的类型,同时强制它们都是类型 Funct?

问题

当你这样做时:

Structure<T: Trait>{
    inner: T,
    many: Vec<T>
}

您是在告诉编译器为每个不同的 T 创建一个专门的实例。因此,如果 FooBar 都实现了 Trait,那么编译器将生成两种不同的表示形式,具有两种不同的大小:

struct Foo(u8);
impl Trait for Foo{
    // impl goes here
}

struct Bar(u64);
impl Trait for Bar{
    // impl goes here
}

然后编译器会生成如下内容:

Structure<Foo>{
    inner: Foo,
    many: Vec<Foo>
}

// and 
Structure<Bar>{  
    inner: Bar, 
    many: Vec<Bar>
}

显然你不能将 Foo 个实例放入 Bar 中,因为它们是不同的类型并且具有不同的大小。

解决方案

您需要 Box<> 您的 Funct 类型以使它们具有相同的大小(即指针大小)。通过将它们放在(智能)指针后面,您实际上是在擦除它们的类型:

    let a: Box<dyn Trait> = Box::new(Foo(0));
    let b: Box<dyn Trait> = Box::new(Bar(0));

现在 ab 具有相同的大小(指针的大小)并且具有相同的类型 - Box<dyn Trait>。所以现在你可以这样做:

struct Structure{ // no longer generic!
    inner: Box<dyn Trait>, // can hold any `Box<dyn Trait>`
    many: Vec<Box<dyn Trait>> // can hold any `Box<dyn Trait>`
}

这种方法的缺点是它需要堆分配并且它丢失了 ab 的确切类型。你不再知道 aFoo 还是 Bar 还是其他什么。

您可以使用任何其他智能指针代替 Box,例如 RcArc 如果您需要它的功能。

另一种选择

另一种选择是使 FooBar 具有相同的大小和类型。这可以通过将它们包装在 enum:

中来完成
enum Holder{
    Foo(Foo),
    Bar(Bar),  // any other types go here in their own variants
}

那么您的结构将如下所示:

struct Structure{ // no longer generic!
    inner: Holder, // can hold any Holder variant`
    many: Vec<Holder> // can hold any Holder variant`
}

缺点是您必须实施如下委托:

impl Trait for Holder{
    fn some_method(&self){
        match self{
            Holder::Foo(foo) => foo.some_method(),
            Holder::Bar(bar) => bar.some_method(),
        }
    }
}

match任何你想使用对象的地方。现在你的 Holder 枚举的大小将是 max(sizeof(Foo), sizeof(Bar))

好的方面:

  • 您仍然知道实际类型 - 它没有被删除
  • 没有堆分配