Why can Pin<Box<T>> directly call Future::poll when it expects Pin<&mut Self>

Hi,

I recently encountered a Rust behavior regarding Pin and Future that I’d like to clarify.

Rust

impl Future for TockSubscribe {
    type Output = Result<(u32, u32, u32), ErrorCode>;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        // ...
    }
}

Even though poll takes self: Pin<&mut Self>, the caller holds a Pin<Box<TockSubscribe>> and can still invoke it directly.

Why does this work, and what specific Rust rules (such as deref coercion or blanket implementations) make this possible?

Thanks!

There's a

impl<P> Future for Pin<P>
where
    P: ops::DerefMut<Target: Future>,

which means Pin<Box<TockSubscribe>> also implements Future.

EDIT:

sorry, I misread the question. I thought you were asking about the coercion to &mut Self, but actually you are asking Pin<&mut Self>, which is irrelevant to the DerefMut impl for Pin.

as far as I know, it is not possible to poll a future directly with a Pin<Box<T>>. I suspect either you are calling a different method which happens to be named poll(), or you have a different type than Pin<Box<T>>.

can you provide more code please?

/EDIT


Pin implements DerefMut when it is safe, and Box<T> is one of the special case.

the exact condition under which it is safe is somewhat complicated, but it is an implementation detail to the standard library and the user should expect it to "just work".

see comments for the helper mod for details.

Hi nerditation

The whole code is following

pub fn subscribe_allow_rw<S: Syscalls, C: allow_rw::Config>(

        driver_num: u32,

        subscribe_num: u32,

        buffer_num: u32,

        buffer: &mut \[u8\],

    ) -> Pin<Box<TockSubscribe>> {

        *// Pinning is necessary since we are passing a pointer to the TockSubscribe to the kernel.*

        let mut f = Pin::new(Box::new(TockSubscribe::new()));
    pub fn subscribe_finish(
        f: Pin<Box<TockSubscribe>>,
    ) -> impl Future<Output = Result<(u32, u32, u32), ErrorCode>> {
        f
    }
pub async fn execute_chunked_request(

        &self,

        command: u32,

        response_buffer: &mut \[u8\],

    ) -> Result<usize, MailboxError> {

        let result = share::scope::<(), \_, \_>(|\_handle| {

            let mut sub = TockSubscribe::subscribe_allow_rw::<S, DefaultConfig>(

                self.driver_num,

                mailbox_subscribe::COMMAND_DONE,

                mailbox_rw_buffer::RESPONSE,

                response_buffer,

            );



            match S::command(

                self.driver_num,

                mailbox_cmd::EXECUTE_CHUNKED_REQUEST,

                command,

                0,

            )

            .to_result::<(), ErrorCode>()

            {

                Ok(()) => Ok(TockSubscribe::subscribe_finish(sub)),

                Err(err) => {

                    S::unallow_rw(self.driver_num, mailbox_rw_buffer::RESPONSE);

                    sub.cancel();

                    Err(MailboxError::ErrorCode(err))

                }

            }

        })?

        .await;

&mut is an exclusive loan, and Box is an exclusive owner.

They're pretty much the same thing, except Box isn't limited by a temporary lifetime.

I didn't see any invocations of the .poll() method in the code. maybe your question is not about the Future::pin() method?

if your question is about this function, i.e. why Pin<Box<T>> implements Future:

the answer is given by @quinedot above.

if your question is about the desugar of .await in async functions in general, I think you might be looking for the concept of "pin projection":

the anonymous Future type generated from an async function is structurally pinned, and the .await is desugared into .poll() invocation on the pin-projected sub futures.