The place the lifetime 'b
in Ref<'b, T>
comes from originally is RefCell::borrow()
β it is the lifetime of a reference to the RefCell
. It ensures that the Ref
cannot outlive the RefCell
. However, it can be arbitrarily long β even 'static
β if the caller provides a suitable &'b RefCell<T>
.
But the premise of a RefCell
is that you can borrow the contents only as long as you have the run-time Ref
or RefMut
guard alive. That's how it prevents conflicts. And so any reference to the T
you get must be a borrow of the Ref
, not of the RefCell
.
If Ref::map()
had the signature proposed by @wishawa, then you could use it to break RefCell
by extracting a long-lived &T
:
use std::cell::{RefCell, Ref};
fn cheat<'rc, T>(cell: &'rc RefCell<T>) -> &'rc T {
let guard = cell.borrow();
let mut output = None;
Ref::map(guard, |ref_to_contents| {
output = Some(ref_to_contents);
&()
});
output.unwrap()
}
fn main() {
let cell = RefCell::new(0);
let r1: &i32 = cheat(&cell);
let r2: &mut i32 = &mut *cell.borrow_mut();
// oops, UB: r1 and r2 refer to the same memory but r2 is exclusive
}
There is, however a valid non-HRTB signature you could write:
pub fn map<'r, U: ?Sized, F>(orig: &'r Ref<'b, T>, f: F) -> Ref<'r, U>
where
F: FnOnce(&'r T) -> &'r U
{...}
Here, the f
function is given a reference that is only valid as long as the Ref
exists. But, in order to achieve that, it has to take a borrowed Ref
, and that is less useful than the real Ref::map()
β if you can keep the original Ref
around to borrow it, then you can also just apply the function to the reference the guard dereferences to:
let guard = cell.borrow();
let u: &U = f(&*guard);