[][src]Crate assert_unmoved

A type that asserts that the underlying type is not moved after being pinned and mutably accessed.

This is a rewrite of futures-test's AssertUnmoved to allow use in more use cases. This also supports traits other than futures.

Examples

An example of detecting incorrect Pin::new_unchecked use (should panic):

This example panics
use futures_util::{
    future::{self, Future},
    task::{noop_waker, Context},
};
use assert_unmoved::AssertUnmoved;
use std::pin::Pin;

let waker = noop_waker();
let mut cx = Context::from_waker(&waker);

// First we allocate the future on the stack and poll it.
let mut future = AssertUnmoved::new(future::pending::<()>());
let pinned_future = unsafe { Pin::new_unchecked(&mut future) };
assert!(pinned_future.poll(&mut cx).is_pending());

// Next we move it back to the heap and poll it again. This second call
// should panic (as the future is moved).
let mut boxed_future = Box::new(future);
let pinned_boxed_future = unsafe { Pin::new_unchecked(&mut *boxed_future) };
let _ = pinned_boxed_future.poll(&mut cx).is_pending();

An example of detecting incorrect StreamExt::next implementation (should panic):

This example panics
use futures_util::{
    future::Future,
    stream::{self, Stream},
    task::{noop_waker, Context, Poll},
};
use assert_unmoved::AssertUnmoved;
use std::pin::Pin;

struct Next<'a, S: Stream>(&'a mut S);

impl<S: Stream> Future for Next<'_, S> {
    type Output = Option<S::Item>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        // This is `Pin<&mut Type>` to `Pin<Field>` projection and is unsound
        // if `S` is not `Unpin` (you can move `S` after `Next` dropped).
        //
        // The correct projection is `Pin<&mut Type>` to `Pin<&mut Field>`.
        // In `Next`, it is `Pin<&mut Next<'_, S>>` to `Pin<&mut &mut S>`,
        // and it needs to add `S: Unpin` bounds to convert `Pin<&mut &mut S>`
        // to `Pin<&mut S>`.
        let stream: Pin<&mut S> = unsafe { self.map_unchecked_mut(|f| f.0) };
        stream.poll_next(cx)
    }
}

let waker = noop_waker();
let mut cx = Context::from_waker(&waker);

let mut stream = AssertUnmoved::new(stream::pending::<()>());

{
    let next = Next(&mut stream);
    let mut pinned_next = Box::pin(next);
    assert!(pinned_next.as_mut().poll(&mut cx).is_pending());
}

// Move stream to the heap.
let mut boxed_stream = Box::pin(stream);

let next = Next(&mut boxed_stream);
let mut pinned_next = Box::pin(next);
// This should panic (as the future is moved).
let _ = pinned_next.as_mut().poll(&mut cx).is_pending();

Optional features

  • futures03 — Implements futures 0.3 traits for assert-unmoved types.
  • tokio02 — Implements tokio 0.2 traits for assert-unmoved types.
  • tokio03 — Implements tokio 0.3 traits for assert-unmoved types.

Structs

AssertUnmoved

A type that asserts that the underlying type is not moved after being pinned and mutably accessed.