特征关联类型生命周期和自我
Trait associated type lifetime and self
我有一个结构,它包装了一个 std::cell::Ref
并通过引用提供对基础值的访问。像这样:
use std::cell::Ref;
struct IntAccess<'a> {
i: Ref<'a, i32>,
}
impl IntAccess<'_> {
fn get(&self) -> &i32 {
&self.i
}
}
这很好用。由于我有多个这样的结构,我想定义一个共同的特征:
trait Access {
type Item;
fn get(&self) -> Self::Item;
}
但是,我在尝试为 IntAccess
实施 Access
时遇到了麻烦:
impl<'a> Access for IntAccess<'a> {
type Item = &'a i32;
fn get(&self) -> Self::Item {
&self.i
}
}
失败并出现以下错误:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:23:9
|
23 | &self.i
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
--> src/main.rs:22:12
|
22 | fn get(&self) -> Self::Item {
| ^^^^^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:23:9
|
23 | &self.i
| ^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
--> src/main.rs:19:6
|
19 | impl<'a> Access for IntAccess<'a> {
| ^^
note: ...so that the types are compatible
--> src/main.rs:22:33
|
22 | fn get(&self) -> Self::Item {
| _________________________________^
23 | | &self.i
24 | | }
| |_____^
= note: expected `<IntAccess<'a> as Access>`
found `<IntAccess<'_> as Access>`
我想我有点明白编译器试图告诉我的意思:我正在尝试借用 self.i
,它的匿名生命周期与 Self::Item
的生命周期无关('a
).所以似乎需要的是以某种方式将 get
中的 self
的生命周期与 'a
的生命周期联系起来。有办法吗?
我不得不承认我没有完全把握这个问题。由于我将生命周期 'a
传递给 Ref
,并且 Ref
在内部存储了此生命周期的引用,所以在我看来,应该可以通过某种方式获得生命周期 [=] 的引用19=] 出来,而不必明确地将 self
绑定到此生命周期。那么我在这里缺少什么?
您的理解是正确的。关于你的疑问,让我们命名生命周期以便于调试:
impl<'a> Access for IntAccess<'a> {
type Item = &'a i32;
fn get<'b>(&'b self) -> Self::Item {
&self.i
}
}
self
具有类型 &'b IntAccess<'a>
。 'b
短于或等于 'a
- self
需要 well-formed,即能够存在(否则, 它会包含一个悬空引用)。
因此,我们不能借 self
超过 'b
- 或者我们可以这样做:
let v: IntAccess<'a>;
let inner: &'a i32 = {
let r: &'b IntAccess<'a> = &v;
<IntAccess<'a> as Access>::get(r)
}
let mut_r: &mut IntAccess<'a> = &mut v;
我们同时拥有对 inner
的(部分)的共享和可变引用!
没有 Generic Associated Types 就无法完全解决您的问题。它们允许您通过生命周期参数化关联类型,使其能够表达“我想 return 此关联类型绑定到 self
的生命周期”:
#![feature(generic_associated_types)]
trait Access {
type Item<'a>
where
Self: 'a;
fn get<'a>(&'a self) -> Self::Item<'a>;
// Or, with lifetime elision:
// fn get(&self) -> Self::Item<'_>;
}
impl<'a> Access for IntAccess<'a> {
type Item<'b> = &'b i32
where
'a: 'b;
fn get<'b>(&'b self) -> Self::Item<'b> {
&self.i
}
}
我们可以在稳定版上这样做吗?我们可以效仿。我们不会为 IntAccess
本身实现 Access
,而是为 实现它的引用 !
trait Access {
type Item;
fn get(self) -> Self::Item;
}
impl<'a, 'b> Access for &'b IntAccess<'a> {
type Item = &'b i32;
fn get(self) -> &'b i32 {
&self.i
}
}
这并不总是有效,因此不能完全替代 GAT,但在这种情况下已经足够了。
我有一个结构,它包装了一个 std::cell::Ref
并通过引用提供对基础值的访问。像这样:
use std::cell::Ref;
struct IntAccess<'a> {
i: Ref<'a, i32>,
}
impl IntAccess<'_> {
fn get(&self) -> &i32 {
&self.i
}
}
这很好用。由于我有多个这样的结构,我想定义一个共同的特征:
trait Access {
type Item;
fn get(&self) -> Self::Item;
}
但是,我在尝试为 IntAccess
实施 Access
时遇到了麻烦:
impl<'a> Access for IntAccess<'a> {
type Item = &'a i32;
fn get(&self) -> Self::Item {
&self.i
}
}
失败并出现以下错误:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:23:9
|
23 | &self.i
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
--> src/main.rs:22:12
|
22 | fn get(&self) -> Self::Item {
| ^^^^^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:23:9
|
23 | &self.i
| ^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
--> src/main.rs:19:6
|
19 | impl<'a> Access for IntAccess<'a> {
| ^^
note: ...so that the types are compatible
--> src/main.rs:22:33
|
22 | fn get(&self) -> Self::Item {
| _________________________________^
23 | | &self.i
24 | | }
| |_____^
= note: expected `<IntAccess<'a> as Access>`
found `<IntAccess<'_> as Access>`
我想我有点明白编译器试图告诉我的意思:我正在尝试借用 self.i
,它的匿名生命周期与 Self::Item
的生命周期无关('a
).所以似乎需要的是以某种方式将 get
中的 self
的生命周期与 'a
的生命周期联系起来。有办法吗?
我不得不承认我没有完全把握这个问题。由于我将生命周期 'a
传递给 Ref
,并且 Ref
在内部存储了此生命周期的引用,所以在我看来,应该可以通过某种方式获得生命周期 [=] 的引用19=] 出来,而不必明确地将 self
绑定到此生命周期。那么我在这里缺少什么?
您的理解是正确的。关于你的疑问,让我们命名生命周期以便于调试:
impl<'a> Access for IntAccess<'a> {
type Item = &'a i32;
fn get<'b>(&'b self) -> Self::Item {
&self.i
}
}
self
具有类型 &'b IntAccess<'a>
。 'b
短于或等于 'a
- self
需要 well-formed,即能够存在(否则, 它会包含一个悬空引用)。
因此,我们不能借 self
超过 'b
- 或者我们可以这样做:
let v: IntAccess<'a>;
let inner: &'a i32 = {
let r: &'b IntAccess<'a> = &v;
<IntAccess<'a> as Access>::get(r)
}
let mut_r: &mut IntAccess<'a> = &mut v;
我们同时拥有对 inner
的(部分)的共享和可变引用!
没有 Generic Associated Types 就无法完全解决您的问题。它们允许您通过生命周期参数化关联类型,使其能够表达“我想 return 此关联类型绑定到 self
的生命周期”:
#![feature(generic_associated_types)]
trait Access {
type Item<'a>
where
Self: 'a;
fn get<'a>(&'a self) -> Self::Item<'a>;
// Or, with lifetime elision:
// fn get(&self) -> Self::Item<'_>;
}
impl<'a> Access for IntAccess<'a> {
type Item<'b> = &'b i32
where
'a: 'b;
fn get<'b>(&'b self) -> Self::Item<'b> {
&self.i
}
}
我们可以在稳定版上这样做吗?我们可以效仿。我们不会为 IntAccess
本身实现 Access
,而是为 实现它的引用 !
trait Access {
type Item;
fn get(self) -> Self::Item;
}
impl<'a, 'b> Access for &'b IntAccess<'a> {
type Item = &'b i32;
fn get(self) -> &'b i32 {
&self.i
}
}
这并不总是有效,因此不能完全替代 GAT,但在这种情况下已经足够了。