1 stable release

1.0.0 Apr 11, 2020

#14 in #pin

MIT OR Apache-2.0 OR MPL-2.0

103 lines

A simple !Unpin I/O backend for async-std

This is a wrapper around async-std's Cursor, but this one is !Unpin. I wanted it for tests for async I/O code that was supposed to be able to support both Unpin and !Unpin backends.

See the crate-level documentation for usage info and examples.


A simple !Unpin I/O backend for async-std, designed for use in tests.

This crate provides the PinCursor struct which wraps around async_std::io::Cursor but is explicitly not Unpin. It is a little building block to help write tests where you want to ensure that your own asynchronous IO code behaves correctly when reading from or writing to something that is definitely !Unpin.

  • It can be backed by any Unpin data buffer that can be slotted into async_std::io::Cursor. Usually Vec<u8> or &mut [u8] (e. g. from an array) are used.
  • It implements async_std::io::{Read, Write, Seek}, so you can poll these traits' methods in your own futures.
  • At the same time, it provides several high-level methods through which you can manipulate the PinCursor in a simple async {} block.


# use async_std::task::block_on;
use pin_cursor::PinCursor;
use async_std::io::{prelude::*, Cursor};
use std::io::SeekFrom;
use std::pin::Pin;

// Construct a async_std::io::Cursor however you like...
let mut data: Vec<u8> = Vec::new();
let cursor = Cursor::new(&mut data);
// ... then wrap it in PinCursor and a pinned pointer, thus losing the Unpin privileges.
let mut cursor: Pin<Box<PinCursor<_>>> = Box::pin(PinCursor::wrap(cursor));
// Note that we have to make an owning pointer first -
// making a Pin<&mut PinCursor<_>> directly is impossible!
// (There is a more complex way to allocate on stack - see the features section.)

// Methods of PinCursor mostly return futures and are designed for use in async contexts.
# block_on(
async {
    // You can write!
    assert_eq!(cursor.as_mut().write(&[1u8, 2u8, 3u8]).await.unwrap(), 3);

    // You can seek!
    assert_eq!(cursor.position(), 3);
    assert_eq!(cursor.as_mut().seek(SeekFrom::Start(1)).await.unwrap(), 1);
    assert_eq!(cursor.position(), 1);

    // You can read!
    let mut buf = [0u8; 1];
    assert_eq!(cursor.as_mut().read(buf.as_mut()).await.unwrap(), 1);
    assert_eq!(buf[0], 2);

    // There's also this way of seeking that doesn't involve futures.
    assert_eq!(cursor.as_mut().read(buf.as_mut()).await.unwrap(), 1);
    assert_eq!(buf[0], 1);
# );


The optional feature stackpin enables integration with stackpin, a crate that provides a way to allocate !Unpin structures on stack.

# use pin_cursor::PinCursor;
# use async_std::io::Cursor;
# use std::pin::Pin;
use stackpin::stack_let;

let mut data: Vec<u8> = vec![1, 2];
stack_let!(mut cursor : PinCursor<_> = Cursor::new(&mut data));
let cursor_ptr: Pin<&mut PinCursor<_>> = Pin::as_mut(&mut cursor);

Now you have a correctly pinned PinCursor that's allocated on stack instead of in a box.


~64K SLoC