我需要为一个操作序列创建操作。这些操作具有以下行为。可以对其进行评估,并且在构造时可以通过单个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)
答案 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
的实现者完全实现ParametrizedInit
和UnparametrizedInit
中的一个。
将来,您可能会用枚举替换标记类型,并用关联的const替换关联的类型。但是目前,“ const泛型”还不够完善,因此我们必须通过使用标记类型来走丑陋的路线。我实际上是在my master's thesis (section 4.2, just search for "mutually exclusive")中讨论这些解决方案。