Is this right model for understanding lifetimes
⚓ Rust 📅 2025-11-30 👤 surdeus 👁️ 5I learned (maybe) bit about lifetimes of course with help of others and my own
Is this right model to have
Consider this function
fn aaa<'a, 'b>(arg: &'a mut [u8;2]) -> (&'a mut [u8], &'a mut [u8]) {
let a = unsafe {arg.split_at_mut_unchecked(1)};
(a.0, unsafe {core::slice::from_raw_parts_mut(a.1 as *mut [u8] as *mut u8 as usize as *mut u8, 1)})
}
This function has unbounded lifetime so applying with both parameters ‘a
fn main() {
let mut a = [6u8; 2];
let (b, c) = aaa(&mut a);
c[0] = 9;
drop(a);
let d = c;
}
let mut a = [6u8; 2]; // type has ‘a
let (b, c) = aaa(&mut a); // b and c has type &’a _;
drop(a)
// due to type a, is an array it is move semantics
// Invalidates all type &’a _;
// You are allowed to move types as long it doesn’t conflict
// with using the invalidated references afterwards
// But we are so
let d = c;
// due to Non-lexical lifetimes
// so we are “extending” the lifetime
Conflicts error appear but if we change the function signature
fn aaa<'a, 'b>(arg: &'a mut [u8;2]) -> (&'a mut [u8], &'a mut [u8])
// to
fn aaa<'a, 'b>(arg: &'a mut [u8;2]) -> (&'a mut [u8], &'b mut [u8])
// notice slight change on return parameters
so
fn main() {
let mut a = [6u8; 2]; // type has ‘a
let (b, c) = aaa(&mut a);
// type b has &’a _ while c has &’b _
// Unbounded lifetime!
c[0] = 9;
drop(a);
// Invalidates all &’a _
let d = c;
// c is &’b _ so not invalidated
// Undefined behaviour through use after free
}
Here another example
struct A<'a> {
a: &'a str,
}
impl A<'_> {
fn a<'a, 'b, 'c>(arg: &'a mut A<'b>) -> &'b mut A<'c> {
unsafe {&mut *(arg as *mut _ as usize as *mut _)}
}
fn b<'a, 'b>(arg: &'a mut A<'b>) -> &'a mut A<'b> {
arg
}
}
With associated function “a” being unsound and possible ub
While associated function “b” being safe
Applying it here
fn main() {
let a = String::new(); // has ‘a
let mut aaaaa = A {a: &a}; // contains &’a _
// But now SHOULD have has ‘b but doesn’t
let c = A::a(&mut aaaaa);
// c is SHOULD be type &’b mut _ BUT is not
// c has &’a mut A<‘c> ‘c is unbounded
drop(aaaaa);
// Invalidates &’b _ but there is none
c.a = "aaaaa"
// Another use after free
}
But what interesting is that changing
drop(aaaaa);
// to
drop(a);
It errors as
drop(a)
// Invalidates &’a _
let c = A::a(&mut aaaaa)
// c has type &’a mut A<‘c>
// while the argument has &’b mut A<‘a>
// due to the function signature
Note that:
// Also lifetime annotations differ from the “lifetimes labels” applied by compiler
// e.g
fn a<‘a>(a: &’a u8) -> &’a u8 {
a
}
fn main() {
let local = 5u8;
let one_ref = &local; has type ‘1
let two_ref = &local; has type ‘2
let three_ref = a(two_ref) has type ‘3
}
// &T is copy so copy semantics
// Lifetime annotation differs from actual lifetimes viewed by the compiler
To have even more bad signature such by
changing signature
fn a<'a, 'b, 'c>(arg: &'a mut A<'b>) -> &'b mut A<'c>
// to
fn a<'a, 'b, 'c, 'd>(arg: &'a mut A<'b>) -> &'d mut A<'c>
Both of them are unbounded so dropping either of them will compile
(no good as use after free)
Of course
struct Newtype(u8);
fn main() {
let mut a = Newtype(5u8);
let refe = &mut a;
let I_know_a_guy_whos_know_a_guy = &refe;
drop(a);
// Invalidates &’a _
}
Now this model would dangle the “I_know_a_guy_whos_know_a_guy”
But I am assuming there is the implicit bound of ‘b: ‘a
So its invalidating ‘b as well to prevent dangling references
Bounds, now I understand little bit as such there are other cases of compiler extending lifetimes. I heard it here,
Here this example
struct A<'a> {
a: &'a str,
}
struct Phantom<'a> {
a: core::marker::PhantomData<&'a mut ()>,
}
impl A<'_> {
fn b<'a, 'b, 'c>(arg: &'a mut A<'b>) -> (&'a mut A<'b>, Phantom<'c>)
where
'a: 'c,
{
(
arg,
Phantom {
a: core::marker::PhantomData,
},
)
}
}
fn main() {
let a = String::new();
let mut aaaaa = A { a: &a };
{
let (mut c, mut phantom) = A::b(&mut aaaaa);
aaaaa.a = "aaaaa";
let c = phantom;
}
}
This errors due to the bound of ‘a: ‘c, it can be fixed by commenting on the bound, or commenting on “let c = phantom” (due to non-lexical lifetime) but it can also error if the bound is there (but not “let c = phantom”)
Using
impl Drop for Phantom<'a> {
fn drop(&mut self) {/*...*/}
}
The type will then be dropped at end of scope or a function consuming the type rather than “borrowing” it.
Now I get little of for<‘a> but not alot,
Of course I heard closures are “opaque” syntactic sugar so for<‘a> is used like fn a<‘a>(blah blah blah) -> blah but applied to for<‘a> | blah | { blah }
1 post - 1 participant
🏷️ Rust_feed