Aaron Turon

Tech | Personal | Academic | Music | About


Project maintained by aturon Hosted on GitHub Pages — Theme by mattgraham

Futures 0.2 is nearing release

On behalf of the futures-rs team, I’m very happy to announce that the master branch is now at 0.2: we have a release candidate! Barring any surprises, we expect to publish to crates.io in the next week or two.

You can peruse the 0.2 API via the hosted crate docs, or dive right in to the master branch. Note that Tokio is not currently compatible with Futures 0.2; see below for more detail.

What’s Futures 0.2 about?

The Futures 0.2 release is all about putting us on a road toward 1.0 this year. To that end, it:

  • Makes numerous long-desired API improvements, many of which are breaking changes.

  • Positions the crate for significant iteration this year by (temporarily!) breaking it into a number of independently-versioned subcrates.

The full details are in the three RFCs, but we’ll review the high level changes here.

API improvement: explicit task contexts

The heart of the futures library is its task system. But historically, that task system was almost invisible: information about the current task in 0.1 was provided via implicit context (a thread-local variable):

// The 0.1 API for task contexts:
fn current() -> Task;

While this implicit context had some ergonomic benefits, it was also a major stumbling block for learning futures, and meant that you needed to carefully read documentation to know whether a given function could only be used “in a task context”.

In Futures 0.2, we instead deal with task contexts via an explicit argument:

// Futures in 0.2
trait Future {
    type Item;
    type Error;

    fn poll(&mut self, cx: task::Context) -> Poll<Self::Item, Self::Error>;
}

While we believe the ergonomic hit here is minor, we were also encouraged by an ingenious construction from @seanmonstar showing how to recover the ergonomics of the 0.1 API.

As a happy by-product, the APIs for working with task-local data are now substantially more pleasant, giving direct mutable access to the stored data:

// The 0.2 API for working with task-local data
impl<T> LocalKey<T> {
    fn get_mut<'a>(&'static self, cx: &'a mut Context) -> &'a mut T
}

API improvement: overhauled executors

Executors in 0.2 are vastly simplified compared to 0.1, while supporting a wider range of functionality.

First, we codify that futures are always run in the context of an executor on which they can spawn additional tasks:

// An API on `task::Context`:
impl Context {
    fn spawn<F>(&mut self, f: F) where
        F: Future<Item = (), Error = Never> + 'static + Send;
}

Baking in an executor as part of all task contexts makes it much easier to coordinate execution choices.

The “out of the box” ways of executing futures change as well:

  • The new ThreadPool executor replaces CpuPool as a general purpose task executor, and provides a streamlined set of APIs for getting things running. It provides “M:N” task scheduling.

  • The new LocalPool executor provides single-threaded (“M:1”) task scheduling, which is appropriate for mostly I/O-bound tasks. Since it is single threaded, it supports non-Send tasks. This executor is ultimately intended to replace the old built-in executor in Tokio.

  • The wait methods, which block on futures (and friends), have been replaced with a new top-level block_on function designed to be harder to misuse.

A common theme with the built-in executors is removing footguns from the previous design, either by detecting problematic situations and panicking, or by structuring APIs in a more natural and intuitive way.

Finally, there are a host of simplifications to the way you implement executors. The numerous traits and types of 0.1 now boil down to just two key constructs: the Wake trait, which itself has been simplified, and the Context type, which is very simple to construct, and is all you need to execute a task.

API improvement: core I/O interfaces

The Futures crate will now ship with an async equivalent to std::io, namely AsyncRead and AsyncWrite traits and numerous conveniences for working with them.

These traits previously lived in the tokio-io crate, but they are in no way specific to Tokio as a backing source of I/O. This new setup provides all the core I/O interfaces at the futures level, with the intent that libraries can use them to be event loop agnostic. (Note, however, that codec support will remain in Tokio).

The traits are also updated in several ways:

  • They no longer inherit from Read and Write, eliminating a major source of confusion; instead, there are specific adapters that allow you to pass async I/O objects into sync APIs.

  • The vectored I/O operations are now based on the more foundational iovec library, which allows AsyncRead and AsyncWrite to be object safe, and to decouple from more opinionated buffering stories. Use of e.g. the bytes crate can be layered on top.

API improvements: top-to-bottom cleanup

In addition to the highlights above, a whole host of APIs received minor tweaks, including renamings, generalizations, adjustments for consistency, and so on. With the 0.2 release, we’re clearing out a long backlog of such requests.

The API documentation has also been completely reworked.

Supporting further design iteration

While we’re making a bunch of improvements in this release, there are still some known issues and places where changes are expected (see below for some more detail). Our goal this year is to iterate the crate to a 1.0 state, but we want to minimize ecosystem pain while doing so.

Starting with 0.2, the main futures crate is now a facade that simply re-exports from a number of separate crates. This allows us to decouple the key public APIs–Future, Stream, and the task system–from the myriad other APIs that work with them, versioning them independently. These core APIs are provided by the futures-core crate.

The upshot is that most of the async ecosystem can happily interoperate as long as they agree on a futures-core version; the rest of the futures APIs can usually be used with independent versions without harm. Since futures-core also contains the most stable of the futures APIs, we expect this to cut down on ecosystem coordination pain as we continue to iterate on the peripheral APIs.

To take advantage of this split, libraries are encouraged to use the futures-* crates directly, rather than the facade.

Ultimately, when we reach 1.0, the expectation is that all of these APIs will be re-incorporated into a single futures crate, and the facade will be no more.

More detail about this split is available in the RFC.

When will it be published?

TL;DR: most likely within a couple of weeks.

While we’ve been discussing and vetting the 0.2 changes publicly for some time, it’s important to get some real usage prior to publication. We’ve made substantial progress porting parts of Fuchsia to the new release, and expect to have a complete port soon. We will also be coordinating with the Tokio team, which intends to release a 0.2 to integrate with Futures 0.2.

If you are a Futures user, you are strongly encouraged the look at the docs and, if possible, try porting some code. Please open issues or reach out on #futures if you run into problems!

What’s the road to 1.0?

Concurrent with the release of Futures 0.2, we plan to release an updated version of futures-await that provides async/await notation with full borrowing support, due to @withoutboats’s great work in that area.

Beyond 0.2, there are several areas where further iteration is needed:

  • The initial support for borrowing with async/await will depend on unstable features, and thus will be provided by an external “shim” so that the core futures crate can continue to work on stable Rust. Once the ingredients are stabilized, we will need to update futures-core to remove the shim.

  • Borrowing support will also entail changes to combinators and possibly core traits (particularly Sink); we will need to work through the full set of ramifications.

  • We plan to investigate removing Error from Future and friends, which could clear up some longstanding issues with the combinators.

  • We plan to hone our backpressure story with Sink and bounded channels.

Changes in these areas will go through the RFC process.

Note that some of these changes affect futures-core, meaning that there’s likely to be at least one more disruptive bump before we hit 1.0.

We are also working on a book, Asynchronous Programming in Rust, that will provide comprehensive explanations of the library and how to use it, including exercises and case studies.

How to get involved

The Futures team wants to grow, and as part of 0.2 we’ve been pushing toward Rust-style governance to make it easier to get involved.

At the moment, the most valuable help is review of the 0.2 release candidate. You can report feedback via issues or the #futures IRC channel.

If you’re interested in any of the topics listed above for post-0.2 iteration, or otherwise see areas to improve, please reach out on the tracker or channel, or consider writing an RFC!