[英]How do I implement a trait for a type with "rented" reference
NB: I tried to keep this post as concise as possible, the full code can be found at https://github.com/pchampin/pair_trait .注意:我尽量保持这篇文章的简洁,完整的代码可以在https://github.com/pchampin/pair_trait找到。
I have defined the following trait:我定义了以下特征:
pub trait Pair {
type Item: Borrow<str>;
fn first(&self) -> &Self::Item;
fn second(&self) -> &Self::Item;
}
I have generic implementations of this trait for (T,T)
and [T;2]
for any T implementing Borrow<str>
.对于任何实现
Borrow<str>
(T,T)
我都有(T,T)
和[T;2]
的这个特征的通用实现。
I also have a type, built with the rental crate, containing a String
and two Cow<str>
that borrow from that string:我还有一个类型,用租用的板条箱构建,包含一个
String
和两个从该字符串借用的Cow<str>
:
#[rental(covariant)]
pub struct SelfSustainedPair {
line: String,
pair: (Cow<'line, str>, Cow<'line, str>),
}
I would like this type to implement the Pair
trait above, but I can't find a way to do it.我希望这种类型实现上面的
Pair
特征,但我找不到方法来做到这一点。
impl SelfSustainedPair {
pub fn first(&self) -> &Cow<str> { self.suffix().first() }
pub fn second(&self) -> &Cow<str> { self.suffix().second() }
}
I know that this is not an implementation of the trait, but I just wanted to be sure that I could implement the methods first
and second
.我知道这不是 trait 的实现,但我只是想确保我可以实现方法
first
和second
。 The answer is yes: the code above compiles.答案是肯定的:上面的代码可以编译。
impl Pair for SelfSustainedPair {
type Item = Cow<str>;
fn first(&self) -> &Cow<str> { self.suffix().first() }
fn second(&self) -> &Cow<str> { self.suffix().second() }
}
This fails to compile, with the message "expected lifetime parameter" for the 2nd line.这无法编译,第二行显示消息“预期寿命参数”。
This is frustrating because it is so close to attempt #0 above.这令人沮丧,因为它非常接近上面的#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() }
}
Here the compiler is complaining about an "unconstrainted lifetime parameter" for the first line ( impl<'a>
).在这里,编译器抱怨第一行(
impl<'a>
)的“不受约束的生命周期参数”。
I modified my trait Pair
so that it expects a lifetime parameter.我修改了我的 trait
Pair
以便它需要一个生命周期参数。 Surprisingly, this works, even if the liferime parameter is never used in the definition of the trait...令人惊讶的是,即使在特征的定义中从未使用过 liferime 参数,这也是有效的......
I then wrote:然后我写道:
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() }
}
Now the compiler complains that it "cannot infer an appropriate lifetime for autoref" in both methods...现在编译器抱怨它在这两种方法中“无法推断自动引用的适当生命周期”......
Anyway, my intuition is that this is not the right path: the lifetime for which the returned Cow
can not be specified independantly of that of self
...无论如何,我的直觉是这不是正确的道路:返回的
Cow
的生命周期不能独立于self
的生命周期指定......
Ideally, this is what I would like to write:理想情况下,这就是我想写的:
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() }
}
but obviously, the compiler doesn't know about the self
lifetime.但显然,编译器不知道
self
生命周期。
Unfortunately, the fully intended design is currently unachievable.不幸的是,目前无法实现完全预期的设计。 Still, we can adapt attempt #3 to sort-of work.
不过,我们可以将尝试 #3 调整为某种工作。
The idea of returning a &Self::Item
is slightly ill-formed, because the associated type Item
already represents a borrowed value.返回
&Self::Item
的想法有点格式错误,因为关联类型Item
已经表示一个借用值。 It makes more sense to return it directly:直接返回更有意义:
pub trait Pair {
type Item: Borrow<str>;
fn first(&self) -> Self::Item;
fn second(&self) -> Self::Item;
}
But then you would stumble with the incapability of describing this Item
as a Cow<'a, str>
where 'a
is the lifetime of self
.但是随后您会发现无法将这个
Item
描述为Cow<'a, str>
,其中'a
是self
的生命周期。 Attempt 3 was close to a solution by adding a lifetime parameter to the trait itself, thus making this arbitrary lifetime a higher-ranked argument to the trait.通过向特征本身添加生命周期参数,尝试 3 接近于解决方案,从而使这个任意生命周期成为特征的更高级别的参数。
pub trait Pair<'a> {
type Item: 'a + Borrow<str>;
fn first(&'a self) -> Self::Item;
fn second(&'a self) -> Self::Item;
}
This Pair<'a>
now defines a pair of elements bound to 'a
, and are not necessarily contained by self
.这个
Pair<'a>
现在定义了一对绑定到'a
的元素,并且不一定包含在self
。 One possible implementation:一种可能的实现:
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 Rust Playground 中的完整示例
This approach comes at the expense of polluting all APIs relying on this trait with higher ranked trait bounds, so that we can implement Pair<'a>
for all lifetimes 'a
.这种方法的代价是污染所有依赖于具有更高等级特征边界的特征的 API,因此我们可以为所有生命周期
'a
实现Pair<'a>
。 For example:例如:
fn foo<T>(pair: T)
where
for<'a> T: Pair<'a>,
{
unimplemented!()
}
In order to achieve this 'self
lifetime for constraining the associated type Item
, we need Generic Associated Types (GATs).为了实现用于约束关联类型
Item
'self
生命周期”,我们需要通用关联类型(GAT)。 Once implemented, we'll be able to write something like this:实现后,我们将能够编写如下内容:
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)
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.