互斥性状

时间:2019-09-01 22:11:24

标签: rust associated-types

我需要为一个操作序列创建操作。这些操作具有以下行为。可以对其进行评估,并且在构造时可以通过单个i32(例如Sum)对其进行参数化,也可以完全不进行参数化(例如Id)。

我创建一个特征操作。评价部分微不足道。

trait Operation {
    fn evaluate(&self, operand: i32) -> i32;
}

但是我不知道如何描述第二个需求。第一种选择是简单地让Operation的具体实现处理该行为。

pub struct Id {}

impl Id {
    pub fn new() -> Id {
        Id {}
    }
}

impl Operation for Id {
    fn evaluate(&self, operand: i32) -> i32 {
        operand
    }
}

pub struct Sum {
    augend: i32,
}

impl Sum {
    pub fn new(augend: i32) -> Sum {
        Sum { augend }
    }
}

impl Operation for Sum {
    fn evaluate(&self, addend: i32) -> i32 {
        augend + addend
    }
}

第二个选项是一个带有可选i32的新功能。然后,具体的实现处理可能的冗余输入。我发现这比第一种选择更糟糕。

trait Operation {
    fn evaluate(&self, operand: i32) -> i32;
    fn new(parameter: std::Option<i32>)
}

Google使我发现了互斥的特征:https://github.com/rust-lang/rust/issues/51774。看起来很有希望,但是并不能完全解决我的问题。

有没有办法实现这种行为?

trait Operation = Evaluate + (ParametrizedInit or UnparametrizedInit)

1 个答案:

答案 0 :(得分:3)

如何使用关联类型定义初始化数据?

trait Operation {
    type InitData;
    fn init(data: Self::InitData) -> Self;

    fn evaluate(&self, operand: i32) -> i32;
}

impl Operation for Id {
    type InitData = ();
    fn init(_: Self::InitData) -> Self {
        Id {}
    }

    fn evaluate(&self, operand: i32) -> i32 {
        operand
    }
}

impl Operation for Sum {
    type InitData = i32;
    fn init(augend: Self::InitData) -> Self {
        Sum { augend }
    }

    fn evaluate(&self, addend: i32) -> i32 {
        augend + addend
    }
}

对于Id情况,您指定()表示初始化不需要数据。调用Operation::init(())还是有点麻烦,但我认为该特性至少可以很好地体现逻辑。


要真正获得互斥的特征(显然是您想要的),您必须使用一些解决方法。 Rust语言本身不支持互斥特征。但是您可以使用关联的类型和某些标记类型来获得相似的结果。这有点奇怪,但目前可以使用。

trait InitMarker {}

enum InitFromNothingMarker {}
enum InitFromI32Marker {}

impl InitMarker for InitFromNothingMarker {}
impl InitMarker for InitFromI32Marker {}

trait Operation {
    type InitData: InitMarker;

    fn init() -> Self
    where
        Self: Operation<InitData = InitFromNothingMarker>;
    fn init_from(v: i32) -> Self
    where
        Self: Operation<InitData = InitFromI32Marker>;
}

trait UnparametrizedInit: Operation<InitData = InitFromNothingMarker> {}
trait ParametrizedInit: Operation<InitData = InitFromI32Marker> {}

impl<T: Operation<InitData = InitFromNothingMarker>> UnparametrizedInit for T {}
impl<T: Operation<InitData = InitFromI32Marker>> ParametrizedInit for T {}

(理想情况下,您希望拥有一个在板条箱的私有子模块中定义的Sealed特性。那样,除您之外,没有人可以实现该特性。然后使Sealed InitMarker的超级特征。)

这是很多代码,但是至少您可以确保Operation的实现者完全实现ParametrizedInitUnparametrizedInit中的一个。

将来,您可能会用枚举替换标记类型,并用关联的const替换关联的类型。但是目前,“ const泛型”还不够完善,因此我们必须通过使用标记类型来走丑陋的路线。我实际上是在my master's thesis (section 4.2, just search for "mutually exclusive")中讨论这些解决方案。