0.3.0-alpha.3 requires a recent nightly (2018-08-13 or newer):

$ rustup update

Compatibility layer

A compatibility layer between 0.3 and 0.1 was developed. It is now possible to convert an 0.3 future into an 0.1 future and vice versa. Similar conversions for streams and sinks are also supported. Additionally, it is now possible to run 0.3 futures and async functions on Tokio’s executor. We have a dedicated blog post coming up that explains this in more detail.

Spawning

Spawn trait

The std::task::Executor trait is now called std::task::Spawn. We chose to rename the trait to Spawn because the functionality exposed by this trait is not the ability to execute, but the ability to spawn.

As a consequence of this renaming, some functions on Context were adjusted:

impl<'a> Context<'a> {
    pub fn new(local_waker: &'a LocalWaker, spawner: &'a mut dyn Spawn) -> Context<'a> { ... }

    pub fn spawner(&mut self) -> &mut dyn Spawn { ... }

    pub fn with_spawner<'b, Sp: Spawn>(&'b mut self, spawner: &'b mut Sp) -> Context<'b> { ... }

    // ...
}

Spawn macros

This release of futures-rs introduces the spawn! and spawn_with_handle! macros for use in async functions, closures and blocks. Both macros let you spawn a task that polls the given future to completion. The task is spawned via the current context’s spawner.

spawn! lets you spawn futures with Output = ():

#![feature(async_await, await_macro, futures_api)]
use futures::spawn;

spawn!(future).unwrap();

spawn_with_handle! lets you spawn futures with any Output: Send type and returns a future that resolves to the output of the spawned future:

use futures::spawn_with_handle;

let join_handle = spawn_with_handle!(future).unwrap();
let output = await!(join_handle);

spawn! is slightly cheaper than spawn_with_handle! since it doesn’t need to provide a way to get the output.

Should spawning fail, a SpawnError error is returned which contains information about why it failed.

So what futures can be spawned? There are some requirements: The future needs to be Send and 'static and the output needs to be Send. Here’s why:

  • The 'static lifetime means that the future cannot contain any non-static references. This is required so that the spawned task can be run independently to the task that spawns it.
  • The Send bounds mean that the future and its output can be sent to other threads. This is required so that multithreaded executors can run the spawned task on any thread they choose.

Note: You can still execute non-Send, non-'static futures concurrently via the join! macro.

Spawn methods

For use outside of async functions, there’s also something new: The SpawnExt trait. It provides the methods spawn and spawn_with_handle:

use futures::task::SpawnExt;

// spawn()
spawner.spawn(future).unwrap();

// spawn_with_handle()
let join_handle = spawner.spawn_with_handle(future).unwrap();

Inside poll, you can use these methods by accessing the context’s spawner via Context::spawner:

fn poll(self: PinMut<Self>, cx: &mut task::Context) -> Poll<T> {
    cx.spawner().spawn(future).unwrap();
}

The plan: spawn with dyn Future

We plan to eventually replace SpawnExt::spawn with Spawn::spawn. This method will live directly in the Spawn trait defined in libcore. It’s signature will look like this:

fn spawn(
    &mut self,
    future: dyn Future<Output = ()> + Send + 'static,
) -> Result<(), SpawnError>

This method can’t be implemented just yet due to compiler limitations:

  • Arbitrary self types are currently not object-safe: Future::poll uses PinMut as arbitrary self type. This unfortunately makes it impossible to create trait objects for Future. Therefore, Box<dyn Future> and &dyn Future are currently not supported.
  • Unsized rvalues are not yet available: Trait objects (dyn Trait) are !Sized because the size of a trait object depends on the size of the concrete type behind it and can therefore only be known at runtime. Currently we often use Box<dyn Trait> which stores the trait object on the heap to circumvent that. The unsized rvalues feature will make it possible to store !Sized types on the stack.

To circumvent these limitations, we currently have FutureObj and Spawn::spawn_obj. These are, however, just a temporary solution until we can introduce the planned spawn method. The planned spawn method will make it possible to have one fewer heap allocation because the future is kept on the stack for as long as possible.

pin-utils crate

The pin macros now live in their own crate, the pin-utils crate. The macros are general purpose and can be used in any project that uses the pinning API.

The crate includes the macros for (un)pin projections unsafe_pinned! and unsafe_unpinned! and the macro for stack pinning pin_mut!:

let foo = Foo { ... };
pin_mut!(foo); // PinMut<Foo>

Version 0.1.0-alpha.1 is now available on crates.io and the API docs are available on docs.rs.

Further changes

A complete list of changes can be found in our changelog.

A big thanks goes to every one who contributed to this release! This includes @tinaun, @Nemo157, @cramertj and @MajorBreakfast and our new contributors @gbonik and @dbcfd!