On the bottom of a rabbit hole, I encountered this error message which might be the most unexpected message I have ever seen from rustc since we first for acquainted years ago. Would value if someone could help me understand it.
Undoubtedly my code is incorrect. However I can not understand exactly why, and the error message leaves me puzzled. While I do believe to grasp to concept of lifetimes in theory, I must admit it is one of the areas where I can get stuck for quite a while in practise.
Please consider the following example: (switching the signature of bar()
for its comment makes it compile.)
struct Foo<'borrow_lt> {
value: Option<&'borrow_lt bool>,
}
impl<'silly_lt: 'borrow_lt, 'borrow_lt> Foo<'borrow_lt> {
fn new() -> Self {
Self {
value: None,
}
}
fn new_with_value(value: &'borrow_lt bool) -> Self {
let mut foo = Foo::new();
foo.bar(value);
foo
}
fn bar(&'silly_lt mut self, _value: &bool) {
// fn bar(&mut self, _value: &bool) {
unimplemented!();
}
}
fn main() {
let owned = true;
let _foo = Foo::new_with_value(&owned);
}
My expectation would be that new_with_value()
would return a Foo
with a lifetime which would allow main()
to claim ownership. To quote, or slightly paraphrase, rust-by-example:
<'silly_lt: 'borrow_lt, 'borrow_lt> reads as lifetime 'silly_lt is at least as long as 'borrow_lt.
Surely that constraint should be possible to meet, as everything gets dropped when exiting main()
in this simple example? In other words, it surprises me that the compiler picks different lengths for the two named lifetimes. However instead of successful compilation, the following is returned:
Compiling lifetimes v0.1.0 (/tmp/lifetimes)
error[E0515]: cannot return value referencing local variable `foo`
--> src/main.rs:15:9
|
14 | foo.bar(value);
| -------------- `foo` is borrowed here
15 | foo
| ^^^ returns a value referencing data owned by the current function
error[E0505]: cannot move out of `foo` because it is borrowed
--> src/main.rs:15:9
|
5 | impl<'silly_lt: 'borrow_lt, 'borrow_lt> Foo<'borrow_lt> {
| ---------- lifetime `'borrow_lt` defined here
...
14 | foo.bar(value);
| -------------- borrow of `foo` occurs here
15 | foo
| ^^^
| |
| move out of `foo` occurs here
| returning this value requires that `foo` is borrowed for `'borrow_lt`
Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
error: could not compile `lifetimes` due to 2 previous errors
Obviously I'm missing something. What's with the talk about borrowing? All immediately related data is owned, or isn't it? How can I understand which lifetime actually is picked, and why is it too limited? My best guess is that maybe lifetimes are cut short for other (implicit) reasons whenever constraints are coerced? Possibly whenever a variable is moved, returned from a function, or what? (Or does 'a: 'b really only mean make 'a equal to 'b? Is it possible to get the compiler to elaborate on how it comes up with lifetimes?)
Maybe this has been asked a thousand times already? I did search the forum, yet found nothing. Pointers to either direct answers, guides on how to think like our compiler or any other helpful insights are most welcome!