I have this contrived example:
struct Data;
struct Ref<'s> {
x: &'s Data,
}
impl<'s> Ref<'s> {
pub fn as_ref(&self) -> &'s Data {
self.x
}
}
Ref
is supposed to be a lightweight newtype
wrapper over a reference of a given lifetime 's
. Everything works.
Now I need to actually wrap a mutable reference, so this becomes:
struct Data;
struct Ref<'s> {
x: &'s mut Data,
}
impl<'s> Ref<'s> {
pub fn as_ref(&self) -> &'s mut Data {
self.x
}
}
But now everything explodes:
error: lifetime may not live long enough
--> src/logic/test.rs:9:9
|
7 | impl<'s> Ref<'s> {
| -- lifetime `'s` defined here
8 | pub fn as_ref(&self) -> &'s mut Data {
| - let's call the lifetime of this reference `'1`
9 | self.x
| ^^^^^^ method was supposed to return data with lifetime `'s` but it is returning data with lifetime `'1`
error[E0596]: cannot borrow `*self.x` as mutable, as it is behind a `&` reference
--> src/logic/test.rs:9:9
|
9 | self.x
| ^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: consider changing this to be a mutable reference
|
8 | pub fn as_ref(&mut self) -> &'s mut Data {
| ~~~~~~~~~
I don't have a clue of what's happening here. The first error suggests that self.x
is borrowed from self
, but I don't understand why the lifetime of &self
matters here since self.x
is already a reference with a very specific lifetime.
If I fix the second error as suggested, i.e. putting &mut self
, the second error disappears, but:
Ref
, I'm returning one of its fields as-is without mutating anythingWhat you are running into are the rules set in place to ensure that &mut T
s stay exclusive.
First, shared-ness is transitive. An immutable reference can always be shared, therefore an immutable reference to a mutable reference can always be shared, so that mutable reference cannot be exclusive. The error you get from the compiler is that you can't mutate "behind a &
reference" so effectively the mutable reference is no better than an immutable one.
If this were not the case, multiple shared references to Ref
(totally allowed) would be able to get exclusive access to x
at the same time (not allowed):
struct Data;
struct Ref<'s> {
x: &'s mut Data,
}
impl<'s> Ref<'s> {
pub fn as_ref(&self) -> &'s mut Data {
self.x
}
}
fn main() {
let mut data = Data;
let data_ref = Ref { x: &mut data };
let data_ref_1 = &data_ref;
let data_ref_2 = &data_ref;
let data_ref_3 = &data_ref;
let data_mut_1 = data_ref_1.as_ref();
let data_mut_2 = data_ref_2.as_ref();
let data_mut_3 = data_ref_3.as_ref();
// wow! three mutable references to the same thing!
*data_mut_1 = ...;
*data_mut_2 = ...;
*data_mut_3 = ...;
}
Second, mutable references derived from another mutable reference can only satisfy that other lifetime. This one is harder to justify at a glance, but the essense is that the inner mutable reference was only accessible through an outer mutable reference and thus the outer mutable reference must be bound while the inner one is used. Rust conveys this relationship ("must be bound") via lifetime annotations, so you must write:
pub fn as_ref(&'a mut self) -> &'a mut Data { ... }
It does not really matter that the Data
is backed by a longer lifetime 's
, the returned reference must bind self
.
If it were not the case, then &self
is only bound for that function call and could be called again while the returned reference is still in use:
struct Data;
struct Ref<'s> {
x: &'s mut Data,
}
impl<'s> Ref<'s> {
pub fn as_ref(&mut self) -> &'s mut Data {
self.x
}
}
fn main() {
let mut data = Data;
let mut data_ref = Ref { x: &mut data };
let data_mut_1 = data_ref.as_ref();
let data_mut_2 = data_ref.as_ref();
let data_mut_3 = data_ref.as_ref();
// wow! three mutable references to the same thing!
*data_mut_1 = ...;
*data_mut_2 = ...;
*data_mut_3 = ...;
}
Immutable references do not have these rules and are much more loose with their usage.