为什么链接生存期仅与可变引用有关? [英] Why does linking lifetimes matter only with mutable references?

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

问题描述

几天前,出现了一个问题,其中有人可变引用的类型的链接生存期存在问题,该类型本身包含借来的数据.问题在于为该类型的引用提供了与该类型内部的借用数据具有相同生存期的借用.我试图重现问题:

  struct VecRef<'a>(&'a Vec< u8>);struct VecRefRef<'a>(&'a mut VecRef<'a>);fn main(){令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<'a> 中的约束,使其寿命与所引用的 VecRef<'a> 完全相同./p>

我将可变引用的生存期与 VecRef<'a> 中的借入数据相关联:

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

现在可以使用了.但为什么?我什至还能提供这样的参考? create()中的可变引用 r 的生​​存期为 VecRef<'a> ,而不是'a .为什么问题没有出现在函数 create()的调用端?

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

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

示例代码

这与第一个示例相反,在第一个示例中, VecRefRef<'a> 引用了对 VecRef<'a> 的可变引用.我知道可变引用具有不同的别名规则(根本没有别名),但这与链接的生存期有什么关系?

解决方案

警告:我是从我实际上没有的专业水平上讲的.考虑到这篇文章的篇幅,我很可能错了很多次.

TL; DR:顶级值的生命周期是协变的.参考值的寿命是不变的.

问题介绍

通过将 VecRef<'a> 替换为&'a mut T ,可以大大简化示例.

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

让我们使用以下功能代替 VecRefRef 的构造函数:

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

在继续之前,重要的是要了解如何在Rust中隐式转换生存期.当一个人将一个指针分配给另一个显式标注的名字时,就会发生终身强制.这允许的最明显的事情是缩短顶级指针的寿命.因此,这不是典型的举动.

旁边:我说明确注释"是因为 错误[E0623]:生命周期不匹配->src/main.rs:5:26|4 |fn use_ref_ref<'a,'b>(参考:&'a mut&'b mut()){|------------------|||这两种类型声明了不同的生存期...5 |use_same_ref_ref(参考);|^^^^^^^^^^ ...但是来自引用"的数据在这里流入引用"

一个简单的修补程序

一个人可以通过做来解决

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

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

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

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

更简单的修补程序

一个人可以强制执行'a:'b

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

这意味着外部参照的寿命至少是内部参照的寿命.

不太明显

我希望回答这些问题.

一个不固定的

置位'b:'a 不能解决问题.

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

另一个更令人惊讶的解决方法

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

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

还有一个更令人惊讶的非解决方法!

使内部引用不可变完全没有帮助!

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

为什么?!

原因是...

等等,首先我们介绍方差

计算机科学中两个非常重要的概念是协方差协方差.我不会使用这些名称(我将非常明确地说明我使用的是哪种方式),但是这些名称对于 ║名称│类型│值───╫──────────┼1分val│i32│-1────╫──────────┼2║参考│&'x mut i32│0x1

堆栈向下增长,因此 reference 堆栈位置是在 val 之后创建的,并且将在 val 之前删除.

考虑做

 让new_ref =参考; 

获得

 ║名称│类型│值────╫──────────┼────────┼1分val│i32│-1────╫──────────┼────────┼2║参考│&'x mut i32│0x1────╫──────────┼────────┼3║new_ref│&'y mut i32│0x1 

什么寿命对'y 有效?

考虑两个可变的指针操作:

读取阻止'y 增长,因为'x 引用仅保证对象在'范围内保持活动状态x .但是,读取不会阻止'y 缩小,因为在指向值处于活动状态时进行的任何读取都将导致值独立于生存期'y .

写入还可防止'y 增长,因为无法写入无效的指针.但是,写入不会阻止'y 缩小,因为对指针的任何写入都会复制该值,这使其与生命周期'y .

困难的情况-指针指针

使用指针指针考虑一些堆栈位置:

 ║名称│类型│值───╫──────────┼1分val│i32│-1───╫──────────┼2║参考│&'a mut i32│0x1───╫──────────┼3║ref_ref│&'x mut&'a mut i32│0x2 

考虑做

 让new_ref_ref = ref_ref; 

获得

 ║名称│类型│值────╫───────────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 :

读取阻止'y 增长,因为'x 引用仅保证对象在'范围内保持活动状态x .但是,读取不会阻止'y 缩小,因为在指向值处于活动状态时的任何读取都将导致独立于生命周期'y <的值./code>.

写入还可防止'y 增长,因为无法写入无效的指针.但是,写入不会阻止'y 缩小,因为对指针的任何写入都会复制该值,这使其与生命周期'y .

这和以前一样.

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

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

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

由于这种情况,

读取写入也可以防止'b 缩小:

 让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;//糟糕,我们有一个mut i32指针,指向一个删除的值! 

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

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

还记得密码吗?

  fn use_same_ref_ref<'c>(参考:&'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>(参考:&'c mut&'c mut()){}fn use_ref_ref<'a>(reference:&'a mut&'a mut()){use_same_ref_ref(参考);} 

编译器现在需要'a:'a 而不是'a:'b ,这确实是正确的.

第二个直接声明的'a:'b

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

第三个断言的'b:'a

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

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

那不变性呢?

我们在这里有两个案例.首先是使外部引用不可变.

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

这个工作了.为什么?

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

由于这种情况,

读取写入也可以防止'b 缩小:

 让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;//糟糕,我们有一个mut i32指针,指向一个删除的值! 

Ergo,'b 不能缩小,也不能从'a 增长,所以'a =='b 恰好.

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

失败的人?

使内部引用不可变没有帮助:

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

当您认为前面提到的问题从不涉及内部引用的任何读物时,这是有道理的.实际上,这是经过修改的有问题的代码,以证明这一点:

 让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;//糟糕,我们有一个指向删除值的i32指针! 

还有一个问题

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

一个人可以强制执行'a:'b

  fn use_same_ref_ref<'c>(参考:&'c mut&'c mut()){}fn use_ref_ref<'a:'b,'b>(reference:&'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 reference =& mut val;让ref_ref =& mut reference;use_ref_ref(ref_ref); 

其中定义了 use_ref_ref 的地方

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

代码如何执行'a:'b ?在检查时看起来像是相反的情况!

好吧,请记住

 让参考=& 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?

解决方案

Warning: I'm speaking from a level of expertise that I don't really have. Given the length of this post, I'm probably wrong a large number of times.

TL;DR: Lifetimes of top-level values are covariant. Lifetimes of referenced values are invariant.

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.

Aside: I say "explicitly annotated" because in implicit cases like let x = y or fn f<T>(_: T) {}, reborrowing doesn't seem to happen. It is not clear whether this is intended.

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:

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

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.

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!

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆