在traums中使用traits作为类型

wil*_*amg 1 polymorphism enums traits rust

这是我的代码:

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

pub enum Expression {
    UnaryOp(UnaryOperator, Expression),
    Value(i64)
}
Run Code Online (Sandbox Code Playgroud)

这给出了以下错误:

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
Run Code Online (Sandbox Code Playgroud)

我不知道如何实现我想要的.我试过了:

trait UnaryOperator: Sized {
    ...
}
Run Code Online (Sandbox Code Playgroud)

以及

pub enum Expression {
    UnaryOp(UnaryOperator + Sized, Expression),
    ...
}
Run Code Online (Sandbox Code Playgroud)

并没有解决问题.

我已经看到了通过泛型可能实现我想要的方法,但是看起来两个表达式与不同的运算符将是不同的类型,但这不是我想要的.我希望所有表达式都是相同的类型,无论运算符是什么.

She*_*ter 5

性状不具有已知大小-它们是无胶.要了解原因,请查看以下内容:

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 }
}
Run Code Online (Sandbox Code Playgroud)

无论AlphaBeta落实AddOne,所以有多大应该有些武断AddOne?哦,请记住,其他板条箱可能会在将来的某个时间实施您的特性.

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

  1. 用一个Box<Trait>.这与Java这样的语言有点相似但不同,你只接受一个接口.这具有已知的大小(指针的价值)并拥有该特征.这有需要分配的缺点.
    trait UnaryOperator {
        fn apply(&self, expr: Expression) -> Expression;
    }

    pub enum Expression {
        UnaryOp(Box<UnaryOperator>, Expression),
        Value(i64)
    }
Run Code Online (Sandbox Code Playgroud)
  1. 使用对特征的引用.这也有一个已知的大小(一个指针价值两个指针值,见Matthieu M.的评论).缺点是某些东西必须拥有该对象,您需要跟踪生命周期:
    trait UnaryOperator {
        fn apply<'a>(&self, expr: Expression<'a>) -> Expression<'a>;
    }

    pub enum Expression<'a> {
        UnaryOp(&'a UnaryOperator, Expression<'a>),
        Value(i64)
    }
Run Code Online (Sandbox Code Playgroud)
  1. 使用通用.这具有固定的大小,因为枚举的每次使用都将专门用于特定类型.如果您有许多不同的专业化,这会导致代码膨胀.更新正如你指出,这意味着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)
    }
Run Code Online (Sandbox Code Playgroud)

现在,所有这些都失败了,因为你有一个递归类型定义.我们来看看这个简化:

enum Expression {
    A(Expression),
    B(u8),
}
Run Code Online (Sandbox Code Playgroud)

有多大Expression?那么,它需要有足够的空间来容纳...的Expression!哪个需要能够持有Expression......你会看到它的发展方向.

你需要在这里添加一些间接量.适用于#1和#2的类似概念 - 您可以使用Box或引用来获得固定大小:

enum Expression {
    A(Box<Expression>),
    B(u8),
}
Run Code Online (Sandbox Code Playgroud)