我有两个基本相同的特征,但是一个提供了比另一个更低级别的界面。鉴于较高水平的特性,人们可以轻松实现较低水平的特质。我想写一个接受trait的实现的库。
我的具体案例是遍历树的特征:
// "Lower level" version of the trait
pub trait RawState {
type Cost: std::cmp::Ord + std::ops::Add<Output = Self::Cost> + std::marker::Copy;
type CulledChildrenIterator: Iterator<Item = (Self, Self::Cost)>;
fn cull(&self) -> Option<Self::Cost>;
fn children_with_cull(&self) -> Self::CulledChildrenIterator;
}
// "Higher level" version of the trait
pub trait State: RawState {
type ChildrenIterator: Iterator<Item = (Self, Self::Cost)>;
fn children(&self) -> Self::ChildrenIterator;
}
// Example of how RawState could be implemented using State
fn state_children_with_cull<S: State> (s: S)
-> impl Iterator<Item = (S, S::Cost)>
{
s.children()
.filter_map(move |(state, transition_cost)|
state.cull().map(move |emission_cost|
(state, transition_cost + emission_cost)
)
)
}
这里,State trait提供了一个接口,您可以在其中定义.children()函数以列出子项,并使用.cull()
函数来潜在地剔除状态。
RawState
trait提供了一个接口,您可以在其中定义一个函数.children_with_cull()
,它会遍历子节点并在单个函数调用中剔除它们。这允许RawState
的实现永远不会生成它知道将被剔除的子项。
我想允许大多数用户只实现State
特征,并根据状态实现自动生成RawState
实现。但是,在实施State
时,特征的某些部分仍然是RawState
的一部分,例如
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
struct DummyState {}
impl State for DummyState {
type Cost = u32;
type ChildrenIterator = DummyIt;
fn emission(&self) -> Option<Self::Cost> {
Some(0u32)
}
fn children(&self) -> DummyIt {
return DummyIt {};
}
}
会给出错误,因为类型&#34;成本&#34;在RawState中定义,而不是在State中定义。在潜在的解决方法上,重新定义State内RawState的所有相关部分,即将State定义为
pub trait State: RawState {
type Cost: std::cmp::Ord + std::ops::Add<Output = Self::Cost> + std::marker::Copy;
type ChildrenIterator: Iterator<Item = (Self, Self::Cost)>;
fn cull(&self) -> Option<Self::Cost>;
fn children(&self) -> Self::ChildrenIterator;
}
然后编译器会抱怨模糊的重复定义。例如,在DummyState
的{{1}}实施中,它会抱怨State
含糊不清,因为它无法判断您是指Self::Cost
还是{ {1}}。
答案 0 :(得分:5)
考虑到RawState
和State
都不是object-safe(因为他们在回复类型中使用Self
),我会假设你没有&RawState
。 t打算为这些特征创建特征对象(即没有State: RawState
)。
在处理特征对象时,超级边界Copy
最重要,因为特征对象只能指定一个特征(加上标准库中没有方法的少数白名单特征,如Send
,Sync
和&State
)。特征对象引用的vtable仅包含指向该特征中定义的方法的指针。但是如果特征具有超级边界,那么这些特征的方法也包含在vtable中。因此,children_with_cull
(如果合法)可以让您访问State
。
supertrait绑定很重要的另一种情况是子标记为某些方法提供默认实现。默认实现可以使用绑定的supertrait来访问另一个特征的方法。
由于您无法使用特征对象,并且由于您没有State: RawState
中的方法的默认实现,我认为您不应该声明超级边界{{1}因为它不会增加任何东西(实际上会导致问题)。
使用这种方法,有必要按照您的建议从RawState
复制我们需要实施State
的成员。因此,State
将被定义为:
pub trait State: Sized {
type Cost: std::cmp::Ord + std::ops::Add<Output = Self::Cost> + std::marker::Copy;
type ChildrenIterator: Iterator<Item = (Self, Self::Cost)>;
fn cull(&self) -> Option<Self::Cost>;
fn children(&self) -> Self::ChildrenIterator;
}
(请注意,绑定State: Sized
是必需的,因为我们在Self
中使用ChildrenIterator
。RawState
也需要绑定RawState: Sized
。)
最后,我们可以为实施impl
的所有类型提供RawState
State
impl
。使用此State
,任何实现RawState
的类型都会自动实现impl<T> RawState for T
where
T: State
{
type Cost = <Self as State>::Cost;
type CulledChildrenIterator = std::iter::Empty<(Self, Self::Cost)>; // placeholder
fn cull(&self) -> Option<Self::Cost> { <Self as State>::cull(self) }
fn children_with_cull(&self) -> Self::CulledChildrenIterator {
unimplemented!()
}
}
。
<Self as State>
请注意消除名称冲突名称的语法:RawState
。它已用于我们复制的两个成员,因此State
推迟到set PATH=C:<OpenSSL Install Dir>\bin;%PATH%
set PATH=C:<qtkeychain Clone Dir>;%PATH%
。