在枚举中使用特征作为类型

Using traits as types in enums

这是我的代码:

trait UnaryOperator {
    fn apply(&self, expr: Expression) -> Expression;
}

pub enum Expression {
    UnaryOp(UnaryOperator, Expression),
    Value(i64)
}

给出以下错误:

error: the trait 'core::marker::sized' is not implemented for type 'parser::UnaryOperator'
note: 'parser::UnaryOperator' does not have a constant size known at compile-time

我不确定如何完成我想要的。我试过:

trait UnaryOperator: Sized {
    ...
}

以及

pub enum Expression {
    UnaryOp(UnaryOperator + Sized, Expression),
    ...
}

都没有解决问题。

我已经看到使用泛型可能实现我想要的功能的方法,但似乎两个具有不同运算符的表达式将是不同的类型,但这不是我想要的。无论运算符是什么,我都希望所有表达式都是同一类型。

Traits 没有已知的大小 - 它们 unsized。要了解原因,请查看:

trait AddOne {
    fn add_one(&self) -> u8;
}

struct Alpha {
    a: u8,
}

struct Beta {
    a: [u8; 1024],
}

impl AddOne for Alpha {
    fn add_one(&self) -> { 0 }
}

impl AddOne for Beta {
    fn add_one(&self) -> { 0 }
}

AlphaBeta 都实现了 AddOne,那么 任意 AddOne 应该有多大 ?哦,请记住,其他板条箱可能会在未来某个时候实现您的特性。

这就是您遇到第一个错误的原因。有 3 个主要解决方案(请注意 none 这些解决方案会立即解决您的问题...):

  1. 使用 Box<Trait>。这与 Java 之类的语言有点相似但又有所不同,在这些语言中您只需要接受一个接口。它有一个已知的大小(一个指针的值)并且拥有这个特征。这有需要分配的缺点。
    trait UnaryOperator {
        fn apply(&self, expr: Expression) -> Expression;
    }

    pub enum Expression {
        UnaryOp(Box<UnaryOperator>, Expression),
        Value(i64)
    }
  1. 使用对特征的引用。这也有一个已知的大小(一个指针的值两个指针的值,参见)。缺点是 something 必须拥有该对象,您需要跟踪生命周期:
    trait UnaryOperator {
        fn apply<'a>(&self, expr: Expression<'a>) -> Expression<'a>;
    }

    pub enum Expression<'a> {
        UnaryOp(&'a UnaryOperator, Expression<'a>),
        Value(i64)
    }
  1. 使用泛型。它具有固定大小,因为枚举的每次使用都专门用于特定类型。如果您有许多不同的专业化,这有导致代码膨胀的缺点。 Update 正如您所指出的,这意味着 Expression<A>Expression<B> 将具有不同的类型。根据您的使用情况,这可能是个问题。如果两者兼有,您将无法轻松创建Vec<Expression<A>>
    trait UnaryOperator {
        fn apply<U>(&self, expr: Expression<U>) -> Expression<U>;
    }

    pub enum Expression<U>
        where U: UnaryOperator
    {
        UnaryOp(U, Expression<U>),
        Value(i64)
    }

现在,所有这些都像写的那样失败了,因为你有一个递归类型定义。让我们看看这个简化:

enum Expression {
    A(Expression),
    B(u8),
}

Expression有多大?好吧,它需要有足够的 space 来容纳……一个 Expression!这需要能够容纳 Expression.. 你知道这是怎么回事。

您需要在此处添加一些间接性。与 #1 和 #2 类似的概念适用 - 您可以使用 Box 或引用来获得固定大小:

enum Expression {
    A(Box<Expression>),
    B(u8),
}