"simple" 和 "advanced" 版本的 Rust 特性
Rust trait with "simple" and "advanced" versions
我有两个基本相同的特征,但一个提供的接口比另一个低。给定更高级别的特征,可以轻松实现较低级别的特征。我想编写一个接受任一特征实现的库。
我的具体情况是遍历树的特征:
// "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() 函数来列出 children,以及 .cull()
函数来潜在地剔除一个状态。
RawState
特性提供了一个接口,您可以在其中定义一个函数 .children_with_cull()
,它遍历 children 并在单个函数调用中剔除它们。这允许 RawState
的实现甚至永远不会生成它知道将被剔除的 children。
我想让大多数用户只实现 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 {};
}
}
会报错,因为"Cost"类型定义在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;
}
但是编译器会抱怨模棱两可的重复定义。例如,在 State
的 DummyState
实现中,它会抱怨 Self::Cost
不明确,因为它无法判断您指的是 <Self as State>::Cost
还是 [=27] =].
考虑到 RawState
和 State
都不是 object-safe(因为它们在 return 类型中使用 Self
),我假设您不打算为这些特征创建特征对象(即没有&RawState
)。
supertrait 绑定 State: RawState
在处理 trait 对象时最为重要,因为 trait 对象只能指定一个 trait(加上 select 一些来自标准库的没有方法的白名单 trait,例如 Copy
、Send
和 Sync
)。 trait 对象引用的 vtable 仅包含指向该 trait 中定义的方法的指针。但是如果特征有超特征边界,那么来自这些特征的方法也包含在 vtable 中。因此,&State
(如果它是合法的)将允许您访问 children_with_cull
.
超特征绑定很重要的另一种情况是当子特征为某些方法提供默认实现时。默认实现可以利用绑定到从另一个特征访问方法的超特征。
因为你不能使用 trait 对象,而且你没有 State
中方法的默认实现,我认为你不应该简单地声明 supertrait 绑定 State: RawState
,因为它什么也没增加(事实上,会导致问题)。
使用这种方法,有必要按照您的建议从 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
是必需的,因为我们在 ChildrenIterator
中使用 Self
。RawState
还需要绑定 RawState: Sized
。)
最后,我们可以为所有实现 State
的类型提供 impl
的 RawState
。使用此 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
.
我有两个基本相同的特征,但一个提供的接口比另一个低。给定更高级别的特征,可以轻松实现较低级别的特征。我想编写一个接受任一特征实现的库。
我的具体情况是遍历树的特征:
// "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() 函数来列出 children,以及 .cull()
函数来潜在地剔除一个状态。
RawState
特性提供了一个接口,您可以在其中定义一个函数 .children_with_cull()
,它遍历 children 并在单个函数调用中剔除它们。这允许 RawState
的实现甚至永远不会生成它知道将被剔除的 children。
我想让大多数用户只实现 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 {};
}
}
会报错,因为"Cost"类型定义在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;
}
但是编译器会抱怨模棱两可的重复定义。例如,在 State
的 DummyState
实现中,它会抱怨 Self::Cost
不明确,因为它无法判断您指的是 <Self as State>::Cost
还是 [=27] =].
考虑到 RawState
和 State
都不是 object-safe(因为它们在 return 类型中使用 Self
),我假设您不打算为这些特征创建特征对象(即没有&RawState
)。
supertrait 绑定 State: RawState
在处理 trait 对象时最为重要,因为 trait 对象只能指定一个 trait(加上 select 一些来自标准库的没有方法的白名单 trait,例如 Copy
、Send
和 Sync
)。 trait 对象引用的 vtable 仅包含指向该 trait 中定义的方法的指针。但是如果特征有超特征边界,那么来自这些特征的方法也包含在 vtable 中。因此,&State
(如果它是合法的)将允许您访问 children_with_cull
.
超特征绑定很重要的另一种情况是当子特征为某些方法提供默认实现时。默认实现可以利用绑定到从另一个特征访问方法的超特征。
因为你不能使用 trait 对象,而且你没有 State
中方法的默认实现,我认为你不应该简单地声明 supertrait 绑定 State: RawState
,因为它什么也没增加(事实上,会导致问题)。
使用这种方法,有必要按照您的建议从 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
是必需的,因为我们在 ChildrenIterator
中使用 Self
。RawState
还需要绑定 RawState: Sized
。)
最后,我们可以为所有实现 State
的类型提供 impl
的 RawState
。使用此 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
.