Reputation: 33
I'm trying to put a borrowed value behind a Mutex
but I'm having trouble with the borrow checker. Here's a simplified code that demonstrate the problem I encounter:
use std::{
marker::PhantomData,
sync::{Arc, Mutex},
};
struct Test<'a> {
d: PhantomData<Mutex<&'a u8>>,
}
impl<'a> Test<'a> {
pub fn new() -> Self {
Test { d: PhantomData }
}
pub fn test(&'a self) {}
}
fn main() {
let t = Arc::new(Test::new());
let t2 = t.clone();
std::thread::spawn(move || {
t2.test();
});
}
This fails to compile with the following error
error[E0597]: `t2` does not live long enough
--> src/main.rs:21:9
|
19 | let t2 = t.clone();
| -- lifetime `'1` appears in the type of `t2`
20 | std::thread::spawn(move || {
21 | t2.test();
| ^^-------
| |
| borrowed value does not live long enough
| argument requires that `t2` is borrowed for `'1`
22 | });
| - `t2` dropped here while still borrowed
I guess the compiler thinks t2
might be borrowed to somewhere else when calling test()
. It seems if I modify the type of the d
field in struct Test
to anything excluding Mutex
, such as d: Option<&'a u8>
, it works fine. What is so special about Mutex since it's just a wrapper around an UnsafeCell
?
Upvotes: 3
Views: 293
Reputation: 969
What is so special about Mutex since it's just a wrapper around an
UnsafeCell
?
The difference is variance.
&'a T
is covariant in the lifetime 'a
: You can coerce an immutable reference with a longer lifetime to one with a strictly shorter lifetime, because it is always safe to pass &'long T
where &'short T
is expected. This is why the code compiles without the UnsafeCell
.
But UnsafeCell<&'a T>
is invariant in 'a
because it has interior mutability: If you could pass UnsafeCell<&'long T>
to code that takes UnsafeCell<&'short T>
, that code could write a short-lived reference into your long-lived cell. So it is not safe to coerce an UnsafeCell
to have a different lifetime.
(The same is true for any type that lets you mutate the reference it contains, e.g. Mutex<&'a T>
or &mut &'a T
.)
Upvotes: 4