本文介绍了为什么链接生命周期仅与可变引用有关?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

几天前,有一个一个问题,其中有人对包含借用数据本身的类型的可变引用的链接生命周期存在问题.问题是提供对类型的引用,借用与类型内借用数据的生命周期相同.我试图重现这个问题:

struct VecRef(&'a Vec);struct VecRefRef<'a>(&'a mut VecRef<'a>);fn 主(){让 v = vec![8u8, 9, 10];让 mut ref_v = VecRef(&v);创建(&mut ref_v);}fn create<'b, 'a>(r: &'b mut VecRef<'a>) {VecRefRef(r);}

示例代码

我在 create() 中明确注释了 'b.这不会编译:

错误[E0623]:生命周期不匹配-->src/main.rs:12:15|11 |fn create<'b, 'a>(r: &'b mut VecRef<'a>) {|------------------|||这两种类型被声明为具有不同的生命周期......12 |VecRefRef(r);|^ ...但是来自`r`的数据在这里流入`r`

生命周期 'b 类似于 'b <'a 并且因此违反了 VecRefRef 中的约束,使其与被引用的 VecRef 具有完全相同的生命周期.

我将可变引用的生命周期与 VecRef 中的借用数据联系起来:

fn create(r: &'a mut VecRef) {VecRefRef(r);}

现在可以了.但为什么?我怎么能提供这样的参考?create() 中的可变引用 r 的生命周期为 VecRef 而不是 'a.为什么问题没有被推到函数 create() 的调用端?

我注意到另一件我不明白的事情.如果我在 VecRefRef<'a> 结构中使用 immutable 引用,那么在提供具有不同生命周期的 'a 的引用时,不知何故不再重要:

struct VecRef(&'a Vec);struct VecRefRef<'a>(&'a VecRef<'a>);//现在是一个不可变的引用fn 主(){让 v = vec![8u8, 9, 10];让 mut ref_v = VecRef(&v);创建(&mut ref_v);}fn create<'b, 'a>(r: &'b mut VecRef<'a>) {VecRefRef(r);}

示例代码

这与第一个示例相反,其中 VecRefRefVecRef 进行可变引用.我知道可变引用有不同的别名规则(根本没有别名),但这与这里链接的生命周期有什么关系?

解决方案

TL;DR:顶级值的生命周期是协变的.引用值的生命周期是不变的.

问题介绍

您可以通过将 VecRef<'a> 替换为 &'a mut T 来显着简化您的示例.

此外,应该删除main,因为谈论一个函数的一般行为比一些特定的生命周期实例更完整.

代替VecRefRef的构造函数,让我们使用这个函数:

fn use_same_ref_ref(参考:&'c mut &'c mut()){}

在我们更进一步之前,了解生命周期如何在 Rust 中隐式转换是很重要的.当将一个指针分配给另一个显式注释的名称时,就会发生生命周期强制.这允许的最明显的事情是缩小顶级指针的生命周期.因此,这不是典型的举动.

旁白:我说明确注释"是因为在像 let x = yfn f<T>(_: T) {} 这样的隐式情况下,似乎不会发生再借用.不清楚这是不是有意的.

完整的例子是

fn use_same_ref_ref(参考:&'c mut &'c mut()){}fn use_ref_ref<'a, 'b>(参考:&'a mut &'b mut()){use_same_ref_ref(参考);}

给出相同的错误:

错误[E0623]:生命周期不匹配-->src/main.rs:5:26|4 |fn use_ref_ref<'a, 'b>(参考:&'a mut &'b mut()){|------------------|||这两种类型被声明为具有不同的生命周期......5 |use_same_ref_ref(参考);|^^^^^^^^^ ...但是来自`reference`的数据在这里流入`reference`

一个微不足道的修复

可以通过做来修复它

fn use_same_ref_ref(参考:&'c mut &'c mut()){}fn use_ref_ref<'a>(参考:&'a mut &'a mut()){use_same_ref_ref(参考);}

因为签名现在在逻辑上是相同的.然而,不明显的是为什么

let mut val = ();让 mut 引用 = &mut val;让 ref_ref = &mut 引用;use_ref_ref(ref_ref);

能够产生一个&'a mut &'a mut().

一个不太重要的修复

可以改为强制执行 'a: 'b

fn use_same_ref_ref(参考:&'c mut &'c mut()){}fn use_ref_ref<'a: 'b, 'b>(参考:&'a mut &'b mut()){use_same_ref_ref(参考);}

这意味着外部引用的生命周期至少与内部引用的生命周期一样长.

不明显

  • 为什么 &'a mut &'b mut () 不能转换为 &'c mut &'c mut (),或

  • 这是否比&'a mut &'a mut()好.

我希望回答这些问题.

非修复

断言 'b: 'a 不能解决问题.

fn use_same_ref_ref(参考:&'c mut &'c mut()){}fn use_ref_ref<'a, 'b: 'a>(参考:&'a mut &'b mut()){use_same_ref_ref(参考);}

另一个更令人惊讶的修复

使外部引用不可变可以解决问题

fn use_same_ref_ref(参考:&'c &'c mut()){}fn use_ref_ref<'a, 'b>(参考:&'a &'b mut()){use_same_ref_ref(参考);}

还有一个更令人惊讶的非修复!

使内部引用不可变根本没有帮助!

fn use_same_ref_ref(参考:&'c mut &'c()){}fn use_ref_ref<'a, 'b>(参考:&'a mut &'b()){use_same_ref_ref(参考);}

但是为什么??!

原因是……

稍等,首先我们讨论差异

计算机科学中两个非常重要的概念是协方差逆变.我不会使用这些名称(我会非常明确地说明我的投射方式)但是这些名称对于 搜索互联网.

在理解这里的行为之前,了解方差的概念非常重要.如果您参加过涵盖此内容的大学课程,或者您可以从其他上下文中记住它,那么您就处于有利地位.不过,您可能仍然会感谢将这个想法与生命联系起来的帮助.

简单的情况——普通指针

考虑一些带有指针的堆栈位置:

 ║ Name │ Type │ Value────╫────────────┼──────────────────────┼────────1 ║ val │ i32 │ -1────╫────────────┼──────────────────────┼────────2 ║ 参考 │ &'x mut i32 │ 0x1

栈向下增长,所以reference栈位置在val之后创建,在val之前被移除.

考虑一下

let new_ref = 引用;

得到

 ║ Name │ Type │ Value────╫──────────────┼──────────────┼────────1 ║ val │ i32 │ -1────╫──────────────┼──────────────┼────────2 ║ 参考 │ &'x mut i32 │ 0x1────╫──────────────┼──────────────┼────────3 ║ new_ref │ &'y mut i32 │ 0x1

哪些生命周期对 'y 有效?

考虑两个可变指针操作:

  • 阅读

Read 阻止 'y 增长,因为 'x 引用只能保证对象在 ' 范围内保持活动状态x.然而,read 并不能阻止 'y 收缩,因为当指向的值处于活动状态时任何读取都会导致一个与生命周期无关的值 'y.

Write 也防止 'y 增长,因为不能写入无效的指针.但是,write 并不能阻止 'y 收缩,因为对指针的任何写入都会复制该值,这使其独立于生命周期 'y>.

硬案例——指针指针

考虑一些带有指针的堆栈位置:

 ║ Name │ Type │ Value────╫────────────┼──────────────────────┼────────1 ║ val │ i32 │ -1────╫────────────┼──────────────────────┼────────2 ║ 参考 │ &'a mut i32 │ 0x1────╫────────────┼──────────────────────┼────────3 ║ ref_ref │ &'x mut &'a mut i32 │ 0x2

考虑一下

让 new_ref_ref = ref_ref;

得到

 ║ Name │ Type │ Value────╫──────────────┼─────────────────────┼────────1 ║ val │ i32 │ -1────╫──────────────┼─────────────────────┼────────2 ║ 参考 │ &'a mut i32 │ 0x1────╫──────────────┼─────────────────────┼────────3 ║ ref_ref │ &'x mut &'a mut i32 │ 0x2────╫──────────────┼─────────────────────┼────────4 ║ new_ref_ref │ &'y mut &'b mut i32 │ 0x2

现在有两个问题:

  1. 哪些生命周期对 'y 有效?

  2. 哪些生命周期对 'b 有效?

让我们首先考虑带有两个可变指针操作的 'y:

  • 阅读

Read 阻止 'y 增长,因为 'x 引用只能保证对象在 ' 范围内保持活动状态x.然而,read 并不能阻止 'y 收缩,因为当指向的值处于活动状态时任何读取都会导致一个与生命周期无关的值 'y.

Write 也防止 'y 增长,因为不能写入无效的指针.但是,write 并不能阻止 'y 收缩,因为对指针的任何写入都会复制该值,这使其独立于生命周期 'y>.

这和以前一样.

现在,考虑带有两个可变指针操作的 'b

Read 阻止 'b 增长,因为如果要从外部指针中提取内部指针,您将能够在 'a 之后读取它 已过期.

Write 也防止 'b 增长,因为如果要从外部指针中提取内部指针,您可以在 之后写入它'a 已过期.

读取写入一起还可以防止'b缩小,因为这种情况:

let ref_ref: &'x mut &'a mut i32 = ...;{//有生命周期 'b,它小于 'a让 new_val: i32 = 123;//将 'a 缩小到 'b让 new_ref_ref: &'x mut &'b mut i32 = ref_ref;*new_ref_ref = &mut new_val;}//new_ref_ref 超出范围,所以 ref_ref 可以再次使用让 ref_ref: &'a mut i32 = *ref_ref;//糟糕,我们有一个 &'a mut i32 指针指向一个被丢弃的值!

因此,'b 不能缩小,也不能从 'a 增长,所以 'a == 'b 正好. 这意味着 &'y mut &'b mut i32 在生命周期 'b 中是不变的.

好的,这能解决我们的问题吗?

还记得代码吗?

fn use_same_ref_ref(参考:&'c mut &'c mut()){}fn use_ref_ref<'a, 'b>(参考:&'a mut &'b mut()){use_same_ref_ref(参考);}

当您调用 use_same_ref_ref 时,将尝试进行强制转换

&'a mut &'b mut() → &'c mut &'c mut()

现在注意 'b == 'c 因为我们讨论了方差.因此我们实际上是在铸造

&'a mut &'b mut() → &'b mut &'b mut()

外面的 &'a 只能收缩.为了做到这一点,编译器需要知道

'a: 'b

编译器不知道这一点,因此编译失败.

我们的其他示例怎么样?

第一个是

fn use_same_ref_ref(参考:&'c mut &'c mut()){}fn use_ref_ref<'a>(参考:&'a mut &'a mut()){use_same_ref_ref(参考);}

编译器现在需要 'a: 'a 而不是 'a: 'b,这是微不足道的.

第二个直接断言'a:'b

fn use_same_ref_ref(参考:&'c mut &'c mut()){}fn use_ref_ref<'a: 'b, 'b>(参考:&'a mut &'b mut()){use_same_ref_ref(参考);}

第三个断言 'b: 'a

fn use_same_ref_ref(参考:&'c mut &'c mut()){}fn use_ref_ref<'a, 'b: 'a>(参考:&'a mut &'b mut()){use_same_ref_ref(参考);}

这不起作用,因为这不是所需的断言.

不变性怎么样?

我们这里有两个案例.第一个是使外部引用不可变.

fn use_same_ref_ref(参考:&'c &'c mut()){}fn use_ref_ref<'a, 'b>(参考:&'a &'b mut()){use_same_ref_ref(参考);}

这个成功了.为什么?

好吧,考虑一下我们之前缩小 &'b 的问题:

读取写入一起还可以防止'b缩小,因为这种情况:

let ref_ref: &'x mut &'a mut i32 = ...;{//有生命周期 'b,它小于 'a让 new_val: i32 = 123;//将 'a 缩小到 'b让 new_ref_ref: &'x mut &'b mut i32 = ref_ref;*new_ref_ref = &mut new_val;}//new_ref_ref 超出范围,所以 ref_ref 可以再次使用让 ref_ref: &'a mut i32 = *ref_ref;//糟糕,我们有一个 &'a mut i32 指针指向一个被丢弃的值!

因此,'b 不能缩小,也不能从 'a 增长,所以 'a == 'b 正好.

这只会发生,因为我们可以将内部引用替换为一些新的、寿命不够长的引用.如果我们不能交换引用,这不是问题.因此缩短内部引用的生命周期是可能的.

失败的那个?

使内部引用不可变无济于事:

fn use_same_ref_ref(参考:&'c mut &'c()){}fn use_ref_ref<'a, 'b>(参考:&'a mut &'b()){use_same_ref_ref(参考);}

当您考虑到之前提到的问题从不涉及任何来自内部引用的读取时,这是有道理的.事实上,这里有问题的代码被修改以证明:

let ref_ref: &'x mut &'a i32 = ...;{//有生命周期 'b,它小于 'a让 new_val: i32 = 123;//将 'a 缩小到 'b让 new_ref_ref: &'x mut &'b i32 = ref_ref;*new_ref_ref = &new_val;}//new_ref_ref 超出范围,所以 ref_ref 可以再次使用让 ref_ref: &'a i32 = *ref_ref;//糟糕,我们有一个 &'a i32 指针指向一个被丢弃的值!

还有一个问题

已经很长时间了,但回想一下:

可以改为强制执行 'a: 'b

fn use_same_ref_ref(参考:&'c mut &'c mut()){}fn use_ref_ref<'a: 'b, 'b>(参考:&'a mut &'b mut()){use_same_ref_ref(参考);}

这意味着外部引用的生命周期至少与内部引用的生命周期一样长.

不明显

  • 为什么 &'a mut &'b mut () 不能转换为 &'c mut &'c mut (),或

  • 这是否比&'a mut &'a mut()好.

我希望回答这些问题.

我们已经回答了第一个问题,但是第二个问题呢?'a: 'b 允许比 'a == 'b 多吗?

考虑一些类型为 &'x mut &'y mut () 的调用者.如果'x : 'y,那么它会自动转换为&'y mut &'y mut().相反,如果 'x == 'y,则 'x : 'y 已经成立!因此,只有当您希望将包含 'x 的类型返回给调用者时,差异才很重要,调用者是唯一能够区分这两者的人.由于这里不是这种情况,所以两者是等价的.

还有一件事

如果你写

let mut val = ();让 mut 引用 = &mut val;让 ref_ref = &mut 引用;use_ref_ref(ref_ref);

其中定义了 use_ref_ref

fn use_ref_ref(参考:&'a mut &'b mut()) {use_same_ref_ref(参考);}

代码如何强制执行'a: 'b?检查结果似乎正好相反!

好吧,记住这一点

let reference = &mut val;

能够缩短其生命周期,因为此时它是外部生命周期.因此,它可以引用比 val 的实际生命周期更小的生命周期,即使指针在该生命周期之外!

A few days ago, there was a question where someone had a problem with linked lifetimes of a mutable reference to a type which contained borrowed data itself. The problem was supplying a reference to the type with a borrow of the same lifetime as the borrowed data inside the type.I tried to recreate the problem:

struct VecRef<'a>(&'a Vec<u8>);

struct VecRefRef<'a>(&'a mut VecRef<'a>);

fn main() {
    let v = vec![8u8, 9, 10];
    let mut ref_v = VecRef(&v);
    create(&mut ref_v);
}

fn create<'b, 'a>(r: &'b mut VecRef<'a>) {
    VecRefRef(r);
}

Example code

I explicitly annotated 'b here in create(). This does not compile:

error[E0623]: lifetime mismatch
  --> src/main.rs:12:15
   |
11 | fn create<'b, 'a>(r: &'b mut VecRef<'a>) {
   |                      ------------------
   |                      |
   |                      these two types are declared with different lifetimes...
12 |     VecRefRef(r);
   |               ^ ...but data from `r` flows into `r` here

The lifetime 'b is something like 'b < 'a and therefore violating the constraint in the VecRefRef<'a> to be of exactly the same lifetime as the referred to VecRef<'a>.

I linked the lifetime of the mutable reference with the borrowed data inside the VecRef<'a>:

fn create<'a>(r: &'a mut VecRef<'a>) {
    VecRefRef(r);
}

Now it works. But why? How was I even able to supply such a reference? The mutable reference r inside create() has the lifetime of VecRef<'a> not 'a. Why wasn't the problem pushed up to the calling side of the function create()?

I noticed another thing I did not understand. If I use an immutable reference inside the VecRefRef<'a> struct, it somehow does not matter any more when supplying a reference with a different lifetime of 'a:

struct VecRef<'a>(&'a Vec<u8>);

struct VecRefRef<'a>(&'a VecRef<'a>); // now an immutable reference

fn main() {
    let v = vec![8u8, 9, 10];
    let mut ref_v = VecRef(&v);
    create(&mut ref_v);
}

fn create<'b, 'a>(r: &'b mut VecRef<'a>) {
    VecRefRef(r);
}

Example code

This works as opposed to the first example where VecRefRef<'a> took a mutable reference to a VecRef<'a>. I know that mutable references have different aliasing rules (no aliasing at all) but what has that to do with the linked lifetimes here?

解决方案

Introducing the problem

You can simplify your example significantly, by replacing VecRef<'a> with &'a mut T.

Further, one should remove main, since it's more complete to talk about the general behaviour of a function than some particular lifetime instantiation.

Instead of VecRefRef's constructor, let's use this function:

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

Before we go further, it's important to understand how lifetimes get implicitly cast in Rust. When one assigns a pointer to another explicitly annotated name, lifetime coercion happens. The most obvious thing this allows is shrinking the lifetime of the top-level pointer. As such, this is not a typical move.

The full example is then

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

which gives the same error:

error[E0623]: lifetime mismatch
 --> src/main.rs:5:26
  |
4 |     fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
  |                                       ------------------
  |                                       |
  |                                       these two types are declared with different lifetimes...
5 |         use_same_ref_ref(reference);
  |                          ^^^^^^^^^ ...but data from `reference` flows into `reference` here

A trivial fix

One can fix it by doing

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a>(reference: &'a mut &'a mut ()) {
    use_same_ref_ref(reference);
}

since the signatures are now logically the same. However, what is not obvious is why

let mut val = ();
let mut reference = &mut val;
let ref_ref = &mut reference;

use_ref_ref(ref_ref);

is able to produce an &'a mut &'a mut ().

A less trivial fix

One can instead enforce 'a: 'b

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

This means that the lifetime of the outer reference is at least as large as the lifetime of the inner one.

It's not obvious

  • why &'a mut &'b mut () is not castable to &'c mut &'c mut (), or

  • whether this is better than &'a mut &'a mut ().

I hope to answer these questions.

A non-fix

Asserting 'b: 'a does not fix the problem.

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b: 'a>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

Another, more surprising fix

Making the outer reference immutable fixes the problem

fn use_same_ref_ref<'c>(reference: &'c &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a &'b mut ()) {
    use_same_ref_ref(reference);
}

And an even more surprising non-fix!

Making the inner reference immutable doesn't help at all!

fn use_same_ref_ref<'c>(reference: &'c mut &'c ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b ()) {
    use_same_ref_ref(reference);
}

BUT WHY??!

And the reason is...

Hold on, first we cover variance

Two very important concepts in computer science are covariance and contravariance. I'm not going to use these names (I'll be very explicit about which way I'm casting things) but those names are still very useful for searching the internet.

It's very important to understand the concept of variance before you can understand the behaviour here. If you've taken a university course that covers this, or you can remember it from some other context, you're in a good position. You might still appreciate the help linking the idea to lifetimes, though.

The simple case - a normal pointer

Consider some stack positions with a pointer:

    ║ Name      │ Type                │ Value
 ───╫───────────┼─────────────────────┼───────
  1 ║ val       │ i32                 │ -1
 ───╫───────────┼─────────────────────┼───────
  2 ║ reference │ &'x mut i32         │ 0x1

The stack grows downwards, so the reference stack position was created after val, and will be removed before val is.

Consider that you do

let new_ref = reference;

to get

    ║ Name      │ Type        │ Value
 ───╫───────────┼─────────────┼───────
  1 ║ val       │ i32         │ -1
 ───╫───────────┼─────────────┼───────
  2 ║ reference │ &'x mut i32 │ 0x1
 ───╫───────────┼─────────────┼───────
  3 ║ new_ref   │ &'y mut i32 │ 0x1

What lifetimes are valid for 'y?

Consider the two mutable pointer operations:

  • Read
  • Write

Read prevents 'y from growing, because a 'x reference only guarantees the object stays alive during the scope of 'x. However, read does not prevent 'y from shrinking since any read when the pointed-to value is alive will result in a value independent of the lifetime 'y.

Write prevents 'y from growing also, since one cannot write to an invalidated pointer. However, write does not prevent 'y from shrinking since any write to the pointer copies the value in, which leaves it independent of the lifetime 'y.

The hard case - a pointer pointer

Consider some stack positions with a pointer pointer:

    ║ Name      │ Type                │ Value
 ───╫───────────┼─────────────────────┼───────
  1 ║ val       │ i32                 │ -1
 ───╫───────────┼─────────────────────┼───────
  2 ║ reference │ &'a mut i32         │ 0x1
 ───╫───────────┼─────────────────────┼───────
  3 ║ ref_ref   │ &'x mut &'a mut i32 │ 0x2

Consider that you do

let new_ref_ref = ref_ref;

to get

    ║ Name        │ Type                │ Value
 ───╫─────────────┼─────────────────────┼───────
  1 ║ val         │ i32                 │ -1
 ───╫─────────────┼─────────────────────┼───────
  2 ║ reference   │ &'a mut i32         │ 0x1
 ───╫─────────────┼─────────────────────┼───────
  3 ║ ref_ref     │ &'x mut &'a mut i32 │ 0x2
 ───╫─────────────┼─────────────────────┼───────
  4 ║ new_ref_ref │ &'y mut &'b mut i32 │ 0x2

Now there are two questions:

  1. What lifetimes are valid for 'y?

  2. What lifetimes are valid for 'b?

Let's first consider 'y with the two mutable pointer operations:

  • Read
  • Write

Read prevents 'y from growing, because a 'x reference only guarantees the object stays alive during the scope of 'x. However, read does not prevent 'y from shrinking since any read when the pointed-to value is alive will result in a value independent of the lifetime 'y.

Write prevents 'y from growing also, since one cannot write to an invalidated pointer. However, write does not prevent 'y from shrinking since any write to the pointer copies the value in, which leaves it independent of the lifetime 'y.

This is the same as before.

Now, consider 'b with the two mutable pointer operations

Read prevents 'b from growing, since if one was to extract the inner pointer from the outer pointer you would be able to read it after 'a has expired.

Write prevents 'b from growing also, since if one was to extract the inner pointer from the outer pointer you would be able to write to it after 'a has expired.

Read and write together also prevent 'b from shrinking, because of this scenario:

let ref_ref: &'x mut &'a mut i32 = ...;

{
    // Has lifetime 'b, which is smaller than 'a
    let new_val: i32 = 123;

    // Shrink 'a to 'b
    let new_ref_ref: &'x mut &'b mut i32 = ref_ref;

    *new_ref_ref = &mut new_val;
}

// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a mut i32 = *ref_ref;
// Oops, we have an &'a mut i32 pointer to a dropped value!

Ergo, 'b cannot shrink and it cannot grow from 'a, so 'a == 'b exactly. This means &'y mut &'b mut i32 is invariant in the lifetime 'b.

OK, does this solve our questions?

Remember the code?

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

When you call use_same_ref_ref, a cast is attempted

&'a mut &'b mut ()  →  &'c mut &'c mut ()

Now note that 'b == 'c because of our discussion about variance. Thus we are actually casting

&'a mut &'b mut ()  →  &'b mut &'b mut ()

The outer &'a can only be shrunk. In order to do this, the compiler needs to know

'a: 'b

The compiler does not know this, and so fails compilation.

What about our other examples?

The first was

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a>(reference: &'a mut &'a mut ()) {
    use_same_ref_ref(reference);
}

Instead of 'a: 'b, the compiler now needs 'a: 'a, which is trivially true.

The second directly asserted 'a: 'b

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

The third asserted 'b: 'a

fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}

fn use_ref_ref<'a, 'b: 'a>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

This does not work, because this is not the needed assertion.

What about immutability?

We had two cases here. The first was to make the outer reference immutable.

fn use_same_ref_ref<'c>(reference: &'c &'c mut ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a &'b mut ()) {
    use_same_ref_ref(reference);
}

This one worked. Why?

Well, consider our problem with shrinking &'b from before:

This can only happen because we can swap the inner reference for some new, insufficiently long lived reference. If we are not able to swap the reference, this is not a problem. Thus shrinking the lifetime of the inner reference is possible.

And the failing one?

Making the inner reference immutable does not help:

fn use_same_ref_ref<'c>(reference: &'c mut &'c ()) {}

fn use_ref_ref<'a, 'b>(reference: &'a mut &'b ()) {
    use_same_ref_ref(reference);
}

This makes sense when you consider that the problem mentioned before never involves any reads from the inner reference. In fact, here's the problematic code modified to demonstrate that:

let ref_ref: &'x mut &'a i32 = ...;

{
    // Has lifetime 'b, which is smaller than 'a
    let new_val: i32 = 123;

    // Shrink 'a to 'b
    let new_ref_ref: &'x mut &'b i32 = ref_ref;

    *new_ref_ref = &new_val;
}

// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a i32 = *ref_ref;
// Oops, we have an &'a i32 pointer to a dropped value!

There was another question

It's been quite long, but think back to:

We've answered the first bullet-pointed question, but what about the second? Does 'a: 'b permit more than 'a == 'b?

Consider some caller with type &'x mut &'y mut (). If 'x : 'y, then it will be automatically cast to &'y mut &'y mut (). Instead, if 'x == 'y, then 'x : 'y holds already! The difference is thus only important if you wish to return a type containing 'x to the caller, who is the only one able to distinguish the two. Since this is not the case here, the two are equivalent.

One more thing

If you write

let mut val = ();
let mut reference = &mut val;
let ref_ref = &mut reference;

use_ref_ref(ref_ref);

where use_ref_ref is defined

fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
    use_same_ref_ref(reference);
}

how is the code able to enforce 'a: 'b? It looks on inspection like the opposite is true!

Well, remember that

let reference = &mut val;

is able to shrink its lifetime, since it's the outer lifetime at this point. Thus, it can refer to a lifetime smaller than the real lifetime of val, even when the pointer is outside of that lifetime!

这篇关于为什么链接生命周期仅与可变引用有关?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 11:24