The example you posted with an use after free is caused by something called covariance. Basically if you have two lifetimes 'long
and 'short
such that 'short
is entirely contained in 'long
, the lifetime 'long
is a subtype of 'short
. The question now becomes, should OutRef<&'long str>
be a subtype of OutRef<&'short str>
? I have annotated your example with lifetimes below:
fn main() {
// 'long starts here
let s1 = String::from("dynamic str");
// 's starts here
let mut s: &'long str = s1.as_str();
// 'short starts here
let at_s: OutRef<'s, &'short str> = OutRef::<'s, &'long str>::from(&mut s);
{
let s = String::from("Short-lived str");
let s_ref: &'short str = s.as_str();
at_s.write(s_ref); // override s with a pointer to the short-lived str
// 'short ends here
}
dbg!(s); // Use after free!
// 's ends here
// 'long ends here
}
playground (without annotated lifetimes)
What happened here is that your type was defined in a way that makes it covariant in T, which means that even though from
produced an OutRef<'s, &'long str>
, that type can be assigned to a variable of type OutRef<'s, &'short str>
as the lifetime is shorter.
In essense, OutRef
acts like &T
when it comes to subtyping of lifetimes. However OutRef
allows modification, so it should really act like an &mut T
instead. You can fix this by providing the correct type in PhantomData
like this:
struct OutRef<'a, T : 'a> {
ptr: ptr::NonNull<T>,
_lifetime: PhantomData<&'a mut T>, // was &'a ()
}
This change will tell the compiler that OutRef
acts like &mut T
wrt. subtyping. Thus OutRef<'s, &'long str>
is no longer a subtype of OutRef<'s, &'short str>
, so when the compiler runs the borrow checker, it sees that the lifetime on the &str
must end after the call to dbg!
, so when you try to assign s_ref
it will complain that the shorter lifetime is no good, and fail with this error:
error[E0597]: `s` does not live long enough
--> src/main.rs:86:20
|
86 | at_s.write(s.as_str());
| ^ borrowed value does not live long enough
87 | }
| - `s` dropped here while still borrowed
88 | dbg!(s);
| - borrow later used here
You can read more about covariance in the nomicon.