读取可变引用与不可变引用具有不同的生命周期语义

Reading mutable references has different lifetime semantics than immutable references

考虑以下代码,其中包装了对根类型 R 的引用。还存储了某种类型 N(avigate),它知道如何为 T.

取消引用 R
use std::ops::Deref;

struct Wrapper<'r, R, N, T>
where
    N: Fn(&'r R) -> &T,
    T: 'static,
{
    r: &'r R,
    n: N,
}

impl<'r, R, N, T> Deref for Wrapper<'r, R, N, T>
where
    N: Fn(&'r R) -> &T,
    T: 'static,
{
    type Target = T;

    fn deref(&self) -> &T {
        let r: &'r R = self.r;
        let t: &'r T = (self.n)(r);
        t
    }
}

现在,如果我们将引用类型 r: &'r R, 更改为可变的 r: &'r mut R, 它不再有效:

use std::ops::Deref;

struct Wrapper<'r, R, N, T>
where
    N: Fn(&'r R) -> &T,
    T: 'static,
{
    r: &'r mut R,
    n: N,
}

impl<'r, R, N, T> Deref for Wrapper<'r, R, N, T>
where
    N: Fn(&'r R) -> &T,
    T: 'static,
{
    type Target = T;

    fn deref(&self) -> &T {
        let r: &'r R = self.r;
        let t: &'r T = (self.n)(r);
        t
    }
}

错误:

error[E0312]: lifetime of reference outlives lifetime of borrowed content...
  --> src/lib.rs:21:24
   |
21 |         let r: &'r R = self.r;
   |                        ^^^^^^
   |
note: ...the reference is valid for the lifetime 'r as defined on the impl at 13:6...
  --> src/lib.rs:13:6
   |
13 | impl<'r, R, N, T> Deref for Wrapper<'r, R, N, T>
   |      ^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 20:5
  --> src/lib.rs:20:5
   |
20 | /     fn deref(&self) -> &T {
21 | |         let r: &'r R = self.r;
22 | |         let t: &'r T = (self.n)(r);
23 | |         t
24 | |     }
   | |_____^

我们使用 nll 得到更好的错误消息:

error: lifetime may not live long enough
  --> src/lib.rs:21:16
   |
13 | impl<'r, R, N, T> Deref for Wrapper<'r, R, N, T>
   |      -- lifetime `'r` defined here
...
20 |     fn deref(&self) -> &T {
   |              - let's call the lifetime of this reference `'1`
21 |         let r: &'r R = self.r;
   |                ^^^^^ type annotation requires that `'1` must outlive `'r

我在 deref 中注释了生命周期,以确保我与编译器在生命周期方面处于同一轨道。 nll 消息特别有趣,因为它说它需要 &self 才能比 'r.

长寿

但这对我来说没有意义,因为如果我们在 deref 上注释生命周期,它应该如下所示:

fn deref<'1>(&'1 self) -> &'1 T;

而是要求 'r: '1,这是由 Wrapper<'r, ...>

隐式给出的

这种直觉似乎适用于第一个示例,但不适用于具有不可变引用的第二个示例。

所以我有两个问题:

  1. 为什么 self.r 是不可变的还是不可变的?无论如何,我无法可变地访问 r,因为 &self 是不可变的。
  2. 1. 是一个基本限制吗,或者代码是否可以通过注释告诉 rustc 我想做什么?

特征类型在它们的泛型参数上是不变的。

考虑这个例子:

struct Test<'a, F: Fn(&'a i32)> {
    i: &'a i32,
    f: F,
}

fn main() {
    let i = 1i32;
    let t = Test { i: &i, f: |&_| {} };

    {
        let j = 2i32;
        (t.f)(&j);
    }

    println!("{:?}", t.i);
}

这将给出错误:

error[E0597]: `j` does not live long enough
  --> src/main.rs:12:15
   |
12 |         (t.f)(&j);
   |               ^^ borrowed value does not live long enough
13 |     }
   |     - `j` dropped here while still borrowed
14 | 
15 |     println!("{:?}", t.i);
   |                      --- borrow later used here

如您所见,类型 Test<'a ...j 的生命周期不统一,因为 Test 包含特征实现类型 N (静态派遣)。结果,它将在 'a 上保持不变,因此 'a 不能被缩短。但是 j 不适合 'a,因此出现错误。

转到您的问题,让我们看一下您的代码的最小版本:

struct Wrapper<'r, R, N>
where
    N: Fn(&'r R),
{
    r: &'r mut R,
    n: N,
}

impl<'r, R, N> Wrapper<'r, R, N>
where
    N: Fn(&'r R),
{
    fn myderef(&self) {
        (self.n)(self.r)
    }
}

这会产生同样的错误:

error[E0312]: lifetime of reference outlives lifetime of borrowed content...
  --> src/lib.rs:14:18
   |
14 |         (self.n)(self.r)
   |                  ^^^^^^
   |
note: ...the reference is valid for the lifetime 'r as defined on the impl at 9:6...
  --> src/lib.rs:9:6
   |
9  | impl<'r, R, N> Wrapper<'r, R, N>
   |      ^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 13:5
  --> src/lib.rs:13:5
   |
13 | /     fn myderef(&self) {
14 | |         (self.n)(self.r)
15 | |     }
   | |_____^

这里到底发生了什么? &self 的生命周期将是 &'shorter_lifetime Wrapper<'r, R, N> 而不是 &'shorter_lifetime Wrapper<'shorter_lifetime, R, N> 类型。 'r 不会缩短为 'shorter_lifetime,因为 Wrapper 由于 N.

将在其通用生命周期参数 'r 上保持不变

现在我们知道了参数类型 &self 的确切含义,让我们看看 myderef() 的主体内部发生了什么。使用 self.r 调用特征类型 N(静态调度)。但是 self.r 是一个可变引用,在传递给 (self.r)() 时会被重新借用。所以现在你有一个位于另一个引用后面的可变引用(self 是一个引用),它需要存在 'rN 根据定义,需要它的输入参数具有 'r 生命周期),因此 &self 也需要存在 'r。但是 &self 的生命周期是 'shorter_lifetime,因此出现错误。

换句话说,如果您将 &'a & 'b mut T'a'b 之间没有子类型关系)作为函数的输入参数,并且编译器允许您重新借用内部引用和 return 它,则违反了借用规则,因为 &mut T 已经在引用后面。外部引用 "owns" 内部引用,主要是因为内部引用是可变的,并且函数需要保证外部引用至少在内部(可变)引用被重新借用时保持不变,否则在函数之后call 会有不止一个可变引用的所有者。

例如,以下代码将无法编译:

fn test<'a, 'b> (i:&'a &'b mut i32) -> &'b i32 {
    &**i
}

但是这个会:

fn test<'a:'b, 'b> (i:&'a &'b mut i32) -> &'b i32 {
    &**i
}

因为可以保证 'a 至少能活到 'b

如果内部引用是不可变的,那么前者也会编译,因为您可以有多个不可变引用。没有外部引用的概念 "owning" 内部引用。

为了编译最小版本,我们必须告诉编译器 &self 也适用于 'r。或者删除 'rN 的输入参数的硬约束(生命周期省略)。

在您的示例中,根据 Deref 的定义,deref() 不允许您在 &self 上指定生命周期。如果在 N 的输入参数上删除 'r 的硬约束,it will compile