Search code examples
rustrust-pin

Why Pin::new_unchecked(&self) worked but not Pin::new_unchecked(&self).as_ref()?


I was writing a StackBox that references a variable allocated on a memory-mapped stack and implemented Drop trait so that StackBox can be used as a reference.

Since I can guarantee that if StackBox is ever created by my code, then the memory-mapped stack must not be modified, I decided to add pin method that returns Pin<&T>.

However I found it weird that Pin::new_unchecked(&self) worked but Pin::new_unchecked(&self).as_ref() doesn't work.

Here's the full code of StackBox:

pub struct StackBox<'a, T> {                      
    ptr: *mut T,                                  
    phantom: PhantomData<&'a T>,                  
}                                                 
impl<'a, T> StackBox<'a, T> {                     
    fn new(ptr: *mut T) -> StackBox<'a, T> {      
        StackBox {                                
            ptr,                                  
            phantom: PhantomData                  
        }                                         
    }                                             
    /// Why does this compile?
    /// Pin::new_unchecked(&self).as_ref() doesn't work here
    pub fn pin(&self) -> Pin<&T> {                
        unsafe { Pin::new_unchecked(&self) }      
    }                                             
}                                                 
impl<'a, T> Drop for StackBox<'a, T> {            
    fn drop(&mut self) {                          
        unsafe {                                  
            self.ptr.drop_in_place();             
        }                                         
    }                                             
}                                                 
impl<'a, T> Deref for StackBox<'a, T> {           
    type Target = T;                              
                                                  
    fn deref(&self) -> &Self::Target {            
        unsafe { & *self.ptr }                    
    }                                             
}                                                 
impl<'a, T> DerefMut for StackBox<'a, T> {        
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { &mut *self.ptr }                 
    }                                             
}                                                 

Solution

  • When you are writing Pin::new_unchecked(&self) you probably mean Pin::new_unchecked(self) because self is already a reference, but the analysis is basically the same:

        pub fn pin(&self) -> Pin<&T> {
            unsafe { Pin::new_unchecked(self) }
        }
    

    You are probably thinking that you are creating a Pin<&StackBox<T>> and then it magically gets converted into a Pin<&T>.

    But that is not really what it is happening. It is actually deducing the type of the function call from the return type, so you are calling Pin::<&T>::new_unchecked() and it is the self that is converted into a &T in this call, by using the deref() impl from below. It is as if:

        pub fn pin(&self) -> Pin<&T> {                
            unsafe { Pin::<&T>::new_unchecked(self.deref()) }
        }                                             
    

    This can be written more obviously as:

        pub fn pin(&self) -> Pin<&T> {                
            unsafe { Pin::new_unchecked(&*self.ptr) }
        }                                             
    

    As to why the version with as_ref() does not work, this is because your self is a &StackBox<T>, not a StackBox<T>. as_ref() is useful to remove a smart-pointer layer, so it can convert a Pin<Box<T>> into a Pin<&T>, for example. But if you start with a Pin<&Box<T>> (that doesn't make too much sense), there is no conversion to be done.