Struct storing and returning shared vs. mutable references
⚓ Rust 📅 2025-08-06 👤 surdeus 👁️ 12Heya!
(First post, please be kind)
I am - desperately - trying to understang why Case 1 works
pub struct Thing<'a> {
data: &'a Data,
}
impl<'a> Thing<'a> {
pub fn get(&self) -> &'a Data {
self.data
}
}
But Case 2 doesn't:
pub struct Thing<'a> {
data: &'a mut Data,
}
impl<'a> Thing<'a> {
pub fn get(&mut self) -> &'a mut Data {
self.data
}
}
Using common-sense, my conclusion is as follows
-
Case 1:
- We know, Thing borrows Data, that must outlive Thing.
- So it's fine to hand-out shared references that are valid
- even after Thing goes out of scope
- but not longer than Data's scope
-
Case 2:
- Thing holds a mutable borrow of Data.
- Access to Data must go through Thing.
- Lifetime 'a must be longer that the anonymous lifetime of &mut self.
- If that case was allowed, we could hand-out multiple mutable references to Data which is bad
Now I am wondering how Rust thinks about this & accepts Case 1 but comes to the conclusion that Case 2 can cause problems:
22 | impl<'a> Thing<'a> {
| -- lifetime `'a` defined here
23 | pub fn get(&mut self) -> &'a mut Data {
| - let's call the lifetime of this reference `'1`
24 | self.data
| ^^^^^^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
- I do believe Case 1 is merely solved by the
Copyimplementation of shared references? So copying a &'a Data gives a &'a Data. Simple enough. - I think to understand that Case 2 error, I first need to understand what
self.datais. Is it an (implicit) re-borrow that "somehow" ties the anonymous lifetime of&mut selfto&'a mut Data... but then realizes'ais not a subtype of the anonymous lifetime? - If 2) was the root-cause of the smartness of the compiler, are there different re-borrow rules for shared and mutable references? Because making Case 1 an explicit re-borrow is still working fine:
pub struct Thing<'a> {
data: &'a Data,
}
impl<'a> Thing<'a> {
pub fn get(&self) -> &'a Data {
&*self.data
}
}
Any pointers what documentation or reference explains how Rust actually comes to the conclusion that Case 2 is bad would be highly appreciated as well, Thank You!
1 post - 1 participant
🏷️ Rust_feed