How the compiler can deduce a lifetime from a raw pointer

Does raw pointers have a lifetime?
If not, how the compiler can deduce a life time from it

The code below, test has a signature like that:

pub fn test<'a, T>(ptr: *mut T)  -> OutRef<'a, T>

It does has a 'a lifetime; but when I call it, I use

//  fn test2<T>(x: &mut MaybeUninit<T>) -> *mut T
let ptr = test2(self);
test(ptr)

How ever, the returned OutRef<'a, T> does remembers the correct lifetime,

    fn as_out_ref(self: &'_ mut MaybeUninit<T>)
                  -> OutRef<'_, T>
    {
        #[allow(unused_unsafe)]
            unsafe {
            let ptr = test2(self);//self.as_mut_ptr();
            test(ptr)
        }
    }
    let mut x = MaybeUninit::<i32>::uninit();
    let at_x = x.as_out_ref();
    drop(x);
    println!("{:?}", *at_x.ptr);

will not be compiled.
But, the code

    let mut n: String = String::from("123");
    let np = lib::test(&mut n as *mut String);
    drop(n);
    unsafe {
       println!("{:?}", *np.ptr);
    }

does passes compiling, and

    pub fn test3<'a, T>(ptr: *mut T, life: &'a String)  -> OutRef<'a, T> {
        OutRef {
            ptr: ptr,
            _lifetime: PhantomData,
        }
    }

    let mut n: String = String::from("123");
    let np = lib::test3(&mut n as *mut String, &n);
    drop(n);
    unsafe {
       println!("{:?}", *np.ptr);
    }

passes too, I don't know why, can anybody help?

Full code:

mod lib {
    use ::core::{
        marker::PhantomData,
        mem::MaybeUninit,
    };
    
    // Invariant: ptr points to a valid `MaybeUninit<T>`
    #[derive(Debug)]
    pub
    struct OutRef<'a, T : 'a> {
        pub ptr: *mut T,
        _lifetime: PhantomData<&'a ()>,
    }
    
    impl<'a, T : 'a> OutRef<'a, T> {
        pub
        fn write (self: OutRef<'a, T>, value: T)
          -> &'a mut T
        {
            unsafe {
                // # Safety
                //
                //   - this is `MaybeUninit<T>::write`
                self.ptr.write(value);
                &mut *self.ptr
            }
        }
    }
  
    pub
    trait MaybeUninitExt<T> {
        fn as_out_ref (self: &'_ mut Self)
          -> OutRef<'_, T>
        ;
    }
    
    pub fn test<'a, T>(ptr: *mut T)  -> OutRef<'a, T> {
        OutRef {
            ptr: ptr,
            _lifetime: PhantomData,
        }
    }
    
    fn test2<T>(x: &mut MaybeUninit<T>) -> *mut T {
        let p = x.as_mut_ptr();
        p
    }
    
    impl<T : Copy> MaybeUninitExt<T> for MaybeUninit<T> {
        fn as_out_ref(self: &'_ mut MaybeUninit<T>)
          -> OutRef<'_, T>
        {
            #[allow(unused_unsafe)]
            unsafe {
                let ptr = test2(self);//self.as_mut_ptr();
                test(ptr)
                // # Safety
                //
                //   - Respects the invariant
                // OutRef {
                //     ptr: self.as_mut_ptr(),
                //     _lifetime: PhantomData,
                // }
            }
        }
    }
    
    impl<'a, T : 'a> From<&'a mut T> for OutRef<'a, T> {
        #[inline]
        fn from (p: &'a mut T)
          -> OutRef<'a, T>
        {
            #[allow(unused_unsafe)]
            unsafe {
                // # Safety
                //
                //   - Respects the invariant, since something
                //     init can be seen as maybeuninit
                OutRef {
                    ptr: p,
                    _lifetime: PhantomData,
                }
            }
        }
    }
}

use lib::{
    MaybeUninitExt,
    OutRef,
};
use ::core::mem::MaybeUninit;

fn write_ft (at_x: OutRef<'_, i32>)
  -> &'_ mut i32
{
    at_x.write(42)
}

fn main ()
{
    let mut n: String = String::from("123");
    let np = lib::test(&mut n as *mut String);
    drop(n);
    unsafe {
       println!("{:?}", *np.ptr);
    }
    
    let mut x = MaybeUninit::<i32>::uninit();
    let at_x = x.as_out_ref();
    drop(x);
    println!("{:?}", *at_x.ptr);
    
    // let x: &mut i32 = write_ft(at_x);
    // assert_eq!(
    //     dbg!(*x),
    //     42,
    // );
}

You have an unbound lifetime, Rust can pick whatever lifetime it wants and usually that's wrong. That's why you usually don't see unbound lifetimes. (Normally you see unbound lifetimes when things get leaked, i.e. Box::leak)

Rust does not ever track lifetimes associated with raw pointers, that's up to you

3 Likes

because as_out_ref is a method so the returned outref has same life time as self, while a function it just ignore the hint &lifetime because it is independent with it?

    pub fn test3<'a, T>(ptr: *mut T, life: &'a String)  -> OutRef<'a, T> {
        OutRef {
            ptr: ptr,
            _lifetime: PhantomData,
        }
    }

    let mut n: String = String::from("123");
    let np = lib::test3(&mut n as *mut String, &n);
    drop(n);
    unsafe {
       println!("{:?}", *np.ptr);
    }

passes too

faint, I figured out. The only reason the code passed compiling because the real code is

    let np = lib::test3(&mut n as *mut String, &n);
    drop(n);
    unsafe {
       // println!("{:?}", *np.ptr);
    }

It doesn't matter that as_out_ref is a method, only that it has a reference (which has a lifetime) as a parameter. This binds the output's lifetime to the parameter's lifetime.

1 Like