In examining How do I box Arc and Mutexed variables?, I ran into an issue where code that looks OK is generating a "does not live long enough" error while constructing a return value from a Mutex
. Simply pulling the lock().unwrap()
access out of the return object removes the error - but I'd like to understand why Rust is complaining about a lifetime issue in this case.
I was able to cut the code down to a very simple reproducer: The first function compiles OK, the second generates the error message, and they're almost identical.
use std::sync::Mutex;
pub struct Response {
resp: String,
}
pub fn get() -> Response {
let body = Mutex::new("a".to_string());
let x: std::sync::MutexGuard<_> = body.lock().unwrap();
Response { resp: x.clone() }
}
pub fn get2() -> Response {
let body = Mutex::new("a".to_string());
Response {
resp: body.lock().unwrap().clone(),
}
}
error[E0597]: `body` does not live long enough
--> src/lib.rs:16:15
|
16 | resp: body.lock().unwrap().clone(),
| ^^^^ borrowed value does not live long enough
17 | }
18 | }
| - `body` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
As pointed out in Stargateur's answer, the reason for this is the lifetime of temporaries. While Rust does not have a full specification yet, the language reference still is quite good and at least gives a hint to understand the behaviour. Here is the relevant part from the section about temporary lifetimes:
[T]he lifetime of temporary values is typically
- the innermost enclosing statement; the tail expression of a block is considered part of the statement that encloses the block, or
- the condition expression or the loop conditional expression if the temporary is created in the condition expression of an
if
or in the loop conditional expression of awhile
expression.
The innermost enclosing statement of body.lock().unwrap()
in the second version of your function is the return expression. The spec above states that this expression is "considered part of the statement that encloses the block", which doesn't really exist in this case, but it still gives the right idea: All variables local to the function body are dropped before any temporaries in the return expression is dropped, so body
is dropped before the MutexGuard that borrows body
. The fix you found makes sure the temporary is dropped before body
, since local variables are dropped roughly in reverse order of their creation.
Using #![feature(nll)]
this give a precise error:
|
19 | resp: body.lock().unwrap().clone(),
| ^^^^----------------
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
20 | }
21 | }
| -
| |
| `body` dropped here while still borrowed
| ... and the borrow might be used here, when that temporary is dropped and runs the `Drop`
| code for type `std::sync::MutexGuard`
|
= note: The temporary is part of an expression at the end of a block. Consider forcing
this temporary to be dropped sooner, before the block's local variables are dropped.
For example, you could save the expression's value in a new local variable `x` and then make
`x` be the expression at the end of the block.
Quite long BTW ;) It's because the temporary is drop after body, you could also do this:
pub fn get3() -> Response {
let body = Mutex::new("a".to_string());
let resp = body.lock().unwrap().clone();
Response {
resp,
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With