令人困惑的借用检查器消息:“终生不匹配”

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数据推送到其中之一?

如果我按照编译器和强制的寿命v1v2对比赛,该函数编译(操场):

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)

然而,进一步检查发现原始代码不必要地复杂,是从 whenv1v2实际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)

在这个看似等效的公式中,不再需要v1v2匹配的寿命。

Fra*_*gné 7

问题是在 上&'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)

该类型的v1v2已经省略掉寿命参数。让我们明确说明:

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