如何为具有“租借”引用的类型实现特征

问题描述 投票:2回答:1

注意:我尽量保持这篇文章的简洁,完整的代码可以在https://github.com/pchampin/pair_trait找到。

The problem

我已经定义了以下特征:

pub trait Pair {
    type Item: Borrow<str>;
    fn first(&self) -> &Self::Item;
    fn second(&self) -> &Self::Item;
}

对于任何实现(T,T)的T,我都有[T;2]Borrow<str>这个特性的通用实现。

我也有一个类型,用rental箱子建造,包含String和两个Cow<str>借用该字符串:

#[rental(covariant)]
pub struct SelfSustainedPair {
    line: String,
    pair: (Cow<'line, str>, Cow<'line, str>),
}

我想这种类型实现上面的Pair特性,但我找不到办法。

Attempt #0

impl SelfSustainedPair {
    pub fn first(&self) -> &Cow<str> { self.suffix().first() }
    pub fn second(&self) -> &Cow<str> { self.suffix().second() }
}

我知道这不是特质的实现,但我只是想确保我可以实现方法firstsecond。答案是肯定的:上面的代码编译。

Attempt #1

impl Pair for SelfSustainedPair {
    type Item = Cow<str>;
    fn first(&self) -> &Cow<str> { self.suffix().first() }
    fn second(&self) -> &Cow<str> { self.suffix().second() }
}

这无法编译,第二行的消息是“期望的生命周期参数”。

这令人沮丧,因为它非常接近于上面的#0。

Attempt #2

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>)的“无约束生命周期参数”。

Attempt #3

我修改了我的特性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指定...

Attempt #4

理想情况下,这是我想写的:

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的生命周期。

rust traits lifetime
1个回答
2
投票

不幸的是,完全有意的设计目前无法实现。尽管如此,我们仍然可以将尝试#3改编为排序工作。

返回&Self::Item的想法略显不合理,因为相关类型Item已经代表借来的价值。直接返回更有意义:

pub trait Pair {
    type Item: Borrow<str>;
    fn first(&self) -> Self::Item;
    fn second(&self) -> Self::Item;
}

但是你会发现无法将这个Item描述为Cow<'a, str>,其中'aself的生命。尝试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(GATs)。一旦实现,我们将能够编写如下内容:

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)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.