rust
rust copied to clipboard
Lifetime mismatch from itself
I found myself encountering error[E0623], but the error message is not understandable and seems buggy: it says some variable's lifetime mismatches from itself.
Minimum Reproducible:
#[derive(Debug)]
enum IntOrRef<'a> {
I(u32),
R(&'a u32),
}
#[derive(Debug)]
struct V<'a> {
v: Vec<IntOrRef<'a>>,
}
fn f(v: &mut V) {
match v.v.last().unwrap() {
IntOrRef::I(i) => {v.v.push(IntOrRef::R(&i));}
IntOrRef::R(_)=> {}
}
}
fn main() {
let mut v = V { v: vec![IntOrRef::I(1)] };
f(&mut v);
dbg!(v);
}
Result:
error[E0623]: lifetime mismatch
--> src/bin/temp.rs:14:37
|
12 | fn f(v: &mut V) {
| ------
| |
| these two types are declared with different lifetimes...
13 | match v.v.last().unwrap() {
14 | IntOrRef::I(i) => {v.v.push(IntOrRef::R(&i));}
| ^^^^^^^^^^^^^^^ ...but data from `v` flows into `v` here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0623`.
For me, this is not understandable, and I do not know how to fix it. In the real code, I want to copy a long-lived variable's short-lived reference to a new one and failed. I wonder:
- How should I annotate lifetimes to get this work?
- What does it mean by "mismatching from itself"?
Meta
rustc --version --verbose
:
rustc 1.35.0 (3c235d560 2019-05-20)
binary: rustc
commit-hash: 3c235d5600393dfe6c36eeed34042efad8d4f26e
commit-date: 2019-05-20
host: x86_64-apple-darwin
release: 1.35.0
LLVM version: 8.0
Im not a 100% sure Im right, so if anyone notices a mistake, please point it out :)
- I dont think you can make this work...
.last()
gives you a reference to the last item of the vec. You try and push onto the vec while this reference is alive. What happens if that push had to reallocate the vector? Now you have a reference to memory that isn't yours anymore, Undefined Behaviour.
You could argue that a u32 implements Copy, so you could just #[derive(Copy, Clone)]
your enum, and copy out of the reference you get from .last()
.
Except what happens next is that you would push a reference to the copy (Being on the stack) onto the vec...and after your function returns, the copy is popped of the stack, and the vector would contain a reference to invalid memory...Undefined Behaviour.
Rust disallows both of these forms of UB, and sometimes it spits out weird error messages.
With nll enabled, the output is:
error: lifetime may not live long enough
--> src/main.rs:14:11
|
13 | fn f(v: &mut V) {
| - - let's call the lifetime of this reference `'1`
| |
| has type `&mut V<'2>`
14 | match v.v.last().unwrap() {
| ^^^^^^^^^^ argument requires that `'1` must outlive `'2`
error[E0502]: cannot borrow `v.v` as mutable because it is also borrowed as immutable
--> src/main.rs:15:28
|
13 | fn f(v: &mut V) {
| - has type `&mut V<'1>`
14 | match v.v.last().unwrap() {
| ----------
| |
| immutable borrow occurs here
| argument requires that `v.v` is borrowed for `'1`
15 | IntOrRef::I(i) => {v.v.push(IntOrRef::R(&i));}
| ^^^ mutable borrow occurs here
A simplified example that gives a similar error:
struct Foo;
fn mut_ref<'a, 'b>(val: &'a mut &'b mut Foo) {
let tmp: &'b mut Foo = *val;
}
Gives the following error:
error[E0623]: lifetime mismatch
--> src/main.rs:9:28
|
8 | fn mut_ref<'a, 'b>(val: &'a mut &'b mut Foo) {
| -------------------
| |
| these two types are declared with different lifetimes...
9 | let tmp: &'b mut Foo = *val;
| ^^^^ ...but data from `val` flows into `val` here
error: aborting due to previous error
It's not clear to me what it means for data from val
to flow into itself.
@Aaron1011 with nll enabled:
error: lifetime may not live long enough
--> src/lib.rs:4:14
|
3 | fn mut_ref<'a, 'b>(val: &'a mut &'b mut Foo) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
4 | let tmp: &'b mut Foo = *val;
| ^^^^^^^^^^^ type annotation requires that `'a` must outlive `'b`
|
= help: consider adding the following bound: `'a: 'b`
What is the soundness argument for this error?
A reason why I believe this is sound:
// For every function foo like
fn foo<'long>(r: &'long mut T) -> &'long mut T { ... }
// It should be possible to write a function bar like
fn bar<'short, 'long: 'short>(p: &'short mut &'long mut T) {
*p = foo(*p); // doesn't compile
}
// Since inlining bar is accepted.
fn main() {
let mut x = Default::default();
let mut r = &mut x;
r = foo(r); // compiles
bar(&mut r); // should be possible
}
So this looks like a type system limitation rather than a soundness error.
Note that this issue seems similar to #62400, #59299, and #44409.