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 }
}
}
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.