FWIW, here are other approaches that can be useful:
The .with_...(|it| { ... })
pattern (CPS)
pub
fn with_borrow_inner_value<R> (self: &'_ Self, f: impl FnOnce(&'_ u8) -> R)
-> R
{
f(&*self.inner_struct.borrow().the_value.borrow())
}
}
fn main ()
{
let my_outer_struct = OuterStruct {
inner_struct: RefCell::new(InnerStruct {
the_value: RefCell::new(5),
}),
};
// This works
let _the_value = *(
my_outer_struct
.inner_struct
.borrow()
.the_value
.borrow()
);
// And this too! :)
let the_value =
my_outer_struct
.with_borrow_inner_value(|it| *it)
;
}
Basically instead of doing let var = my_outer_struct.thingy(); <use var here>
, you do
my_outer_struct.with_thingy(|var| {
<use var here>
});
It's a bit more cumbersome but it does solve most "cannot return a local" problems 
.borrow_mut()
This, of course, is not really what you asked, but know that if in your code using .borrow_mut()
is fine, then a .borrow_mut()
on the outer RefCell
removes any need to .borrow
the inner ones!
- The reason for that is simple:
RefCell
is a construct to yield, out of a shared access, exclusive (&mut
) or shared (&
) access to the wrappee using runtime checks (w.r.t other concurrent accesses that may be happening on this shared value). But out of an already exclusive access, there cannot possibly be such concurrent accesses, so there is no need to perform any runtime checks whatsoever: if the outer .borrow_mut()
succeeds, there is no other access on the innermost RefCell
that could make the borrows on it race with anything.
impl OuterStruct {
pub
fn borrow_inner_value_mut (self: &'_ Self)
-> RefMut<'_, u8>
{
RefMut::map(
self.inner_struct.borrow_mut(),
|it| it.the_value.get_mut(),
)
}
}
fn main ()
{
let my_outer_struct = OuterStruct {
inner_struct: RefCell::new(InnerStruct {
the_value: RefCell::new(5),
}),
};
// This works
let _the_value = *(
my_outer_struct
.inner_struct
.borrow()
.the_value
.borrow()
);
// This works as well
let _the_value = *(
my_outer_struct
.borrow_inner_value_mut()
);
}
OwningHandle
(
with unsafe
)
As @alice pointed out, the issue here is that the return type of your borrow_inner
must contain two Ref
instances (each one with its .drop()
to clear the runtime guards), but the inner object refers to the outer one, so this leads to a self-referential situation that Rust abhors. For this kind of things, OwningHandle
can save us when we know the items are not directly self-referential, i.e., that there is a layer of indirection that makes it so objects can be moved around without invalidating the pointers.
impl OuterStruct {
pub
fn borrow_inner_value<'a> (self: &'a Self)
-> impl ::core::ops::Deref<Target = u8> + 'a
{
OwningHandle::new_with_fn(
// Owner: the `Ref<'a, InnerStruct>`
self.inner_struct.borrow(),
// Borrowing handle: the `Ref<'a, u8>`
|it: *const InnerStruct| -> Ref<'a, u8> {
let it: &'a InnerStruct = unsafe {
// Safety: the pointee will live as long as the owner,
// so the `'a` lifetime is sound.
&*it
};
it.the_value.borrow()
},
)
}
}
fn main ()
{
let my_outer_struct = OuterStruct {
inner_struct: RefCell::new(InnerStruct {
the_value: RefCell::new(5),
}),
};
// This works
let _the_value = *(
my_outer_struct
.inner_struct
.borrow()
.the_value
.borrow()
);
// This works as well
let _the_value = *(
my_outer_struct
.borrow_inner_value()
);
}