use*_*342 6 rust borrow-checker rust-lifetimes
我最近遇到了一条我从未见过的借用检查器消息,我试图理解它。这是重现它的代码(简化的,现实生活中的例子更复杂)-操场:
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { &mut v1 } else { &mut v2 };
dest.push(1);
}
Run Code Online (Sandbox Code Playgroud)
它无法编译并出现以下错误:
error[E0623]: lifetime mismatch
--> src/main.rs:2:44
|
1 | fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
| ------------ ------------ these two types are declared with different lifetimes...
2 | let dest = if which { &mut v1 } else { &mut v2 };
| ^^^^^^^ ...but data from `v2` flows into `v1` here
Run Code Online (Sandbox Code Playgroud)
...接着是另一个关于数据从v1into 流入的内容v2。
我的问题是:这个错误是什么意思?什么是数据流以及它如何在两个变量之间发生,假设代码只是将Copy数据推送到其中之一?
如果我按照编译器和强制的寿命v1和v2对比赛,该函数编译(操场):
error[E0623]: lifetime mismatch
--> src/main.rs:2:44
|
1 | fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
| ------------ ------------ these two types are declared with different lifetimes...
2 | let dest = if which { &mut v1 } else { &mut v2 };
| ^^^^^^^ ...but data from `v2` flows into `v1` here
Run Code Online (Sandbox Code Playgroud)
然而,进一步检查发现原始代码不必要地复杂,是从 whenv1和v2实际Vecs遗留下来的,而不是引用。一个更简单、更自然的变体是不设置dest为&mut v1and &mut v2,而是设置为更简单的v1and v2,它们是一开始的引用。这也可以编译(操场):
fn foo<'a>(mut v1: &'a mut Vec<u8>, mut v2: &'a mut Vec<u8>, which: bool) {
let dest = if which { &mut v1 } else { &mut v2 };
dest.push(1);
}
Run Code Online (Sandbox Code Playgroud)
在这个看似等效的公式中,不再需要v1和v2匹配的寿命。
问题是在 上&'a mut T是不变的T。
首先,让我们看一下工作代码:
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { v1 } else { v2 };
dest.push(1);
}
Run Code Online (Sandbox Code Playgroud)
该类型的v1和v2已经省略掉寿命参数。让我们明确说明:
fn foo<'a, 'b>(v1: &'a mut Vec<u8>, v2: &'b mut Vec<u8>, which: bool) {
let dest = if which { v1 } else { v2 };
dest.push(1);
}
Run Code Online (Sandbox Code Playgroud)
编译器必须弄清楚dest. if表达式的两个分支产生不同类型的值:&'a mut Vec<u8>和&'b mut Vec<u8>。尽管如此,编译器还是能够找出与这两种类型都兼容的类型;让我们称这种类型为&'c mut Vec<u8>,其中'a: 'c, 'b: 'c. &'c mut Vec<u8>这里是一种常见的超类型二者的&'a mut Vec<u8>和&'b mut Vec<u8>,因为无论'a和'b活得比'c(即'c是比任一短/小的寿命'a或'b)。
现在,让我们检查错误的代码:
fn foo<'a, 'b>(v1: &'a mut Vec<u8>, v2: &'b mut Vec<u8>, which: bool) {
let dest = if which { &mut v1 } else { &mut v2 };
dest.push(1);
}
Run Code Online (Sandbox Code Playgroud)
同样,编译器必须弄清楚dest. if表达式的两个分支分别生成类型为:&'c mut &'a mut Vec<u8>和的值&'d mut &'b mut Vec<u8>(其中'c和'd是新鲜的生命周期)。
前面我说的&'a mut T是不变的T。这意味着我们不能改变Tin&'a mut T使得我们可以产生 的子类型或超类型&'a mut T。在这里,T类型是&'a mut Vec<u8>和&'b mut Vec<u8>。他们不是同一类型的,所以我们必须得出这样的结论类型&'c mut &'a mut Vec<u8>和&'d mut &'b mut Vec<u8>无关。因此, 没有有效的类型dest。