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 replacesCpuPool
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-levelblock_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
andWrite
, 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
andAsyncWrite
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
fromFuture
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!