Is this right model for understanding lifetimes

⚓ Rust    📅 2025-11-30    👤 surdeus    👁️ 5      

surdeus

I 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

Read full topic

🏷️ Rust_feed