注意:我试图使这篇文章尽可能简洁,完整的代码可以在https://github.com/pchampin/pair_trait上找到。
我定义了以下特征:
pub trait Pair {
type Item: Borrow<str>;
fn first(&self) -> &Self::Item;
fn second(&self) -> &Self::Item;
}
对于(T,T)
和[T;2]
,对于任何实现Borrow<str>
的T,我都有此特征的通用实现。
我也有一种类型,该类型是用rental板条箱构建的,包含一个String
和两个从该字符串中借用的Cow<str>
:
#[rental(covariant)]
pub struct SelfSustainedPair {
line: String,
pair: (Cow<'line, str>, Cow<'line, str>),
}
我希望这种类型实现上面的Pair
特性,但是我找不到解决方法。
impl SelfSustainedPair {
pub fn first(&self) -> &Cow<str> { self.suffix().first() }
pub fn second(&self) -> &Cow<str> { self.suffix().second() }
}
我知道这不是该特征的实现,
但我只是想确保可以实现方法first
和second
。答案是肯定的:上面的代码可以编译。
impl Pair for SelfSustainedPair {
type Item = Cow<str>;
fn first(&self) -> &Cow<str> { self.suffix().first() }
fn second(&self) -> &Cow<str> { self.suffix().second() }
}
这无法编译,并在第二行显示消息“ expected life parameter”。
这很令人沮丧,因为它与上面的#0尝试非常接近。
impl<'a> Pair for SelfSustainedPair {
type Item = Cow<'a, str>;
fn first(&self) -> &Cow<'a, str> { self.suffix().first() }
fn second(&self) -> &Cow<'a, str> { self.suffix().second() }
}
在这里,编译器抱怨第一行(impl<'a>
)的“生命周期参数不受限制”。
我修改了特征Pair
,以使其期望使用生命周期参数。令人惊讶的是,即使在特征的定义中从未使用过liferime参数,这仍然有效...
然后我写了:
impl<'a> Pair<'a> for SelfSustainedPair {
type Item = Cow<'a, str>;
fn first(&self) -> &Cow<'a, str> { self.suffix().first() }
fn second(&self) -> &Cow<'a, str> { self.suffix().second() }
}
现在,编译器抱怨这两种方法都“无法为autoref推断适当的生存期” ...
无论如何,我的直觉是这不是正确的路径:不能独立于Cow
的寿命来指定返回的self
的寿命...
理想情况下,这是我想写的:
impl Pair for SelfSustainedPair {
type Item = Cow<'self, str>;
fn first(&self) -> &Cow<str> { self.suffix().first() }
fn second(&self) -> &Cow<str> { self.suffix().second() }
}
但是很明显,编译器并不知道self
的生存期。
答案 0 :(得分:2)
不幸的是,目前尚无法实现完全预期的设计。尽管如此,我们仍可以使尝试3适应 sort 工作。
返回&Self::Item
的想法有些不正确,因为关联的类型Item
已经表示借来的值。直接返回它更有意义:
pub trait Pair {
type Item: Borrow<str>;
fn first(&self) -> Self::Item;
fn second(&self) -> Self::Item;
}
但是,您将无法将这个Item
描述为Cow<'a, str>
,而其中'a
是self
的生存期。尝试3通过向特征本身添加一个生命周期参数来接近解决方案,从而使此任意生命周期成为该特征的较高级别的参数。
pub trait Pair<'a> {
type Item: 'a + Borrow<str>;
fn first(&'a self) -> Self::Item;
fn second(&'a self) -> Self::Item;
}
此Pair<'a>
现在定义了一对绑定到'a
的元素,并且不一定包含在self
中。一种可能的实现方式:
impl<'a> Pair<'a> for (String, String) {
type Item = Cow<'a, str>;
fn first(&'a self) -> Self::Item {
Cow::Borrowed(&self.0)
}
fn second(&'a self) -> Self::Item {
Cow::Borrowed(&self.1)
}
}
Full example in the Rust Playground
此方法的代价是污染所有依赖具有较高特质界限的特质的API,以便我们可以在整个生存期Pair<'a>
实施'a
。例如:
fn foo<T>(pair: T)
where
for<'a> T: Pair<'a>,
{
unimplemented!()
}
为了达到'self
的生命周期来约束关联的类型Item
,我们需要Generic Associated Types(GAT)。实施后,我们将可以编写如下内容:
pub trait Pair {
type Item<'a>: Borrow<str>;
fn first(&'a self) -> Self::Item<'a>;
fn second(&'a self) -> Self::Item<'a>;
}
impl Pair for (String, String) {
type Item<'a> = Cow<'a, str>;
fn first(&'a self) -> Self::Item<'a> {
Cow::Borrowed(&self.0)
}
fn second(&'a self) -> Self::Item<'a> {
Cow::Borrowed(&self.1)
}
}