令人费解的借用检查器消息:"lifetime mismatch"
Perplexing borrow checker message: "lifetime mismatch"
我最近遇到了一条我从未见过的借用检查器消息,我正在努力理解它。这是重现它的代码(简化的现实生活中的例子更复杂)- playground:
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { &mut v1 } else { &mut v2 };
dest.push(1);
}
编译失败,出现以下错误:
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
...接着是另一个关于从 v1
流入 v2
的数据。
我的问题是:这个错误是什么意思?什么是数据流?假设代码仅将 Copy
数据推送到其中一个变量,它是如何在两个变量之间发生的?
如果我遵循编译器并强制 v1
和 v2
的生命周期匹配,函数编译 (playground):
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);
}
然而,进一步检查发现原始代码不必要地复杂,是 v1
和 v2
实际 Vec
时留下的,而不是引用。一个更简单和更自然的变体是将 dest
设置为不是 &mut v1
和 &mut v2
,而是更简单的 v1
和 v2
,它们是开始的引用.这也编译 (playground):
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { v1 } else { v2 };
dest.push(1);
}
在这个看似等效的公式中,v1
和 v2
匹配的生命周期不再是必需的。
你这里的是这个错误程序的变体:
fn foo(x: &mut Vec<&u32>, y: &u32) {
x.push(y);
}
错误消息过去有点模糊,但如果您有兴趣,可以更改为 pull request. This is a case of Variance, which you can read more about in the nomicon。这是一个复杂的主题,但我会尽力解释它的快速和简短。
除非您指定生命周期,否则当您从 if 语句中 return &mut v1
或 &mut v2
时,编译器会确定这些类型具有不同的生命周期,因此 return换一种类型。因此,编译器无法确定 dest
的正确生命周期(或类型)。当您显式地将所有生命周期设置为相同时,编译器现在可以理解 if 语句 return 的两个分支具有相同的生命周期,并且可以计算出 dest
.
的类型
在上面的示例中,x 的生命周期与 y 不同,因此类型不同。
问题是 &'a mut T
比 T
多 invariant。
首先,让我们看一下工作代码:
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { v1 } else { v2 };
dest.push(1);
}
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);
}
编译器必须找出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>
的共同 supertype,因为 'a
和 'b
都比 'c
长寿(即 'c
比 'a
或 'b
) 的生命周期 shorter/smaller。
现在,让我们检查错误代码:
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);
}
同样,编译器必须找出 dest
的类型。 if
表达式的两个分支分别产生类型的值:&'c mut &'a mut Vec<u8>
和 &'d mut &'b mut Vec<u8>
(其中 'c
和 'd
是新的生命周期)。
我之前说过 &'a mut T
在 T
上是不变的。这意味着我们无法通过更改 &'a mut T
中的 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
.
没有有效类型
我最近遇到了一条我从未见过的借用检查器消息,我正在努力理解它。这是重现它的代码(简化的现实生活中的例子更复杂)- playground:
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { &mut v1 } else { &mut v2 };
dest.push(1);
}
编译失败,出现以下错误:
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
...接着是另一个关于从 v1
流入 v2
的数据。
我的问题是:这个错误是什么意思?什么是数据流?假设代码仅将 Copy
数据推送到其中一个变量,它是如何在两个变量之间发生的?
如果我遵循编译器并强制 v1
和 v2
的生命周期匹配,函数编译 (playground):
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);
}
然而,进一步检查发现原始代码不必要地复杂,是 v1
和 v2
实际 Vec
时留下的,而不是引用。一个更简单和更自然的变体是将 dest
设置为不是 &mut v1
和 &mut v2
,而是更简单的 v1
和 v2
,它们是开始的引用.这也编译 (playground):
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { v1 } else { v2 };
dest.push(1);
}
在这个看似等效的公式中,v1
和 v2
匹配的生命周期不再是必需的。
你这里的是这个错误程序的变体:
fn foo(x: &mut Vec<&u32>, y: &u32) {
x.push(y);
}
错误消息过去有点模糊,但如果您有兴趣,可以更改为 pull request. This is a case of Variance, which you can read more about in the nomicon。这是一个复杂的主题,但我会尽力解释它的快速和简短。
除非您指定生命周期,否则当您从 if 语句中 return &mut v1
或 &mut v2
时,编译器会确定这些类型具有不同的生命周期,因此 return换一种类型。因此,编译器无法确定 dest
的正确生命周期(或类型)。当您显式地将所有生命周期设置为相同时,编译器现在可以理解 if 语句 return 的两个分支具有相同的生命周期,并且可以计算出 dest
.
在上面的示例中,x 的生命周期与 y 不同,因此类型不同。
问题是 &'a mut T
比 T
多 invariant。
首先,让我们看一下工作代码:
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { v1 } else { v2 };
dest.push(1);
}
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);
}
编译器必须找出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>
的共同 supertype,因为 'a
和 'b
都比 'c
长寿(即 'c
比 'a
或 'b
) 的生命周期 shorter/smaller。
现在,让我们检查错误代码:
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);
}
同样,编译器必须找出 dest
的类型。 if
表达式的两个分支分别产生类型的值:&'c mut &'a mut Vec<u8>
和 &'d mut &'b mut Vec<u8>
(其中 'c
和 'd
是新的生命周期)。
我之前说过 &'a mut T
在 T
上是不变的。这意味着我们无法通过更改 &'a mut T
中的 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
.