在traums中使用traits作为类型

时间:2015-05-19 23:17:24

标签: enums polymorphism rust traits

这是我的代码:

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),
    ...
}

并没有解决问题。

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

1 个答案:

答案 0 :(得分: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 }
}

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

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

  1. 使用Box<Trait>。这与Java这样的语言有点相似但不同,你只接受一个接口。这具有已知的大小(指针的价值)并拥有该特征。这有需要分配的缺点。
  2.     trait UnaryOperator {
            fn apply(&self, expr: Expression) -> Expression;
        }
    
        pub enum Expression {
            UnaryOp(Box<UnaryOperator>, Expression),
            Value(i64)
        }
    
    1. 使用对特征的引用。这也有一个已知的大小(一个指针的价值两个指针值,见Matthieu M.'s comment)。缺点是某些东西必须拥有该对象,您需要跟踪生命周期:
    2.     trait UnaryOperator {
              fn apply<'a>(&self, expr: Expression<'a>) -> Expression<'a>;
          }
      
          pub enum Expression<'a> {
              UnaryOp(&'a UnaryOperator, Expression<'a>),
              Value(i64)
          }
      
      1. 使用通用。这具有固定的大小,因为枚举的每次使用都将专门用于特定类型。如果您有许多不同的专业化,这会导致代码膨胀。 更新正如您所指出的,这意味着Expression<A>Expression<B>会有不同的类型。根据您的使用情况,这可能是一个问题。如果您同时拥有Vec<Expression<A>>,则无法轻松创建{.1}}。
      2.     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有多大?嗯,它需要有足够的空间来容纳...... Expression!哪个需要能够持有Expression ....你会看到它的发展方向。

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

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