为什么链接生存期仅与可变引用有关? [英] Why does linking lifetimes matter only with mutable references?
问题描述
几天前,出现了一个问题,其中有人可变引用的类型的链接生存期存在问题,该类型本身包含借来的数据.问题在于为该类型的引用提供了与该类型内部的借用数据具有相同生存期的借用.我试图重现问题:
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(参考);}
这意味着外部参照的寿命至少是内部参照的寿命.
不太明显
为什么
&'a mut&'b mut()
无法转换为&'c mut&'c mut()
,或这是否优于
&'a mut&'a mut()
.我希望回答这些问题.
一个不固定的
置位
'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
现在有两个问题:
什么寿命有效于
'y
?什么生命周期对
'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); }
I explicitly annotated
'b
here increate()
. 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 theVecRefRef<'a>
to be of exactly the same lifetime as the referred toVecRef<'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
insidecreate()
has the lifetime ofVecRef<'a>
not'a
. Why wasn't the problem pushed up to the calling side of the functioncreate()
?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); }
This works as opposed to the first example where
VecRefRef<'a>
took a mutable reference to aVecRef<'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
orfn 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 ()
, orwhether 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 afterval
, and will be removed beforeval
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:
What lifetimes are valid for
'y
?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 operationsRead 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 ()
, orwhether 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 definedfn 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屋!