特征关联类型生命周期和自我

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
    }
}

Playground.

我们可以在稳定版上这样做吗?我们可以效仿。我们不会为 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
    }
}

Playground

这并不总是有效,因此不能完全替代 GAT,但在这种情况下已经足够了。