# futures-rs: the trait layer and combinators behind Rust async

> futures-rs is the Rust library that defines Stream, join!, select! and the executor pieces that sit under async runtimes. Here is what it gives you, where the no_std surface stops, and how it differs from tokio.

**rust-lang/futures-rs** — Zero-cost asynchronous programming in Rust

- Repository: https://github.com/rust-lang/futures-rs
- Website: https://rust-lang.github.io/futures-rs/
- Stars: 5,929 · Forks: 713
- Language: Rust
- License: Apache-2.0
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/rust-lang-futures-rs

## What futures-rs actually provides that std does not

Rust's standard library gives you the Future trait and the async/await syntax. It does not give you a Stream trait, a way to wait on several futures at once, or an executor to drive them. futures-rs fills that gap. The README describes it as "a library providing the foundations for asynchronous programming in Rust" and lists the key trait definitions like Stream alongside utilities such as join!, select! and various futures combinator methods.

The audience is the library author and the systems programmer more than the application developer. Someone writing an HTTP client, a codec, or a device driver needs a Stream abstraction and needs to be able to await two things concurrently without pulling in a full runtime. That is the niche. An application developer who just wants to serve requests will usually reach for a runtime first and only touch the futures crate for a combinator the runtime does not expose.

## The workspace split: futures-core, futures-util and friends

The repository is a Cargo workspace, and the top-level Cargo.toml lists its members explicitly: futures, futures-core, futures-channel, futures-executor, futures-io, futures-macro, futures-sink, futures-task, futures-util and futures-test, plus test crates and two example directories, examples/functional and examples/imperative.

That split matters when you care about compile time and dependency weight. futures-core carries the minimal trait definitions. futures-util holds the combinators. futures-executor holds the runtime pieces. The umbrella futures crate re-exports them so a single dependency line gets you everything. If you are writing a library and want to keep your own dependency tree small, depending on futures-core or futures-util directly is the narrower move, and the workspace layout is what makes that possible.

The workspace also sets lint policy centrally. The rust lints include missing_debug_implementations, rust_2018_idioms, single_use_lifetimes, unreachable_pub and unexpected_cfgs, with a check-cfg entry for cfg(futures_sanitizer). Several clippy lints are allowed with an explicit priority, including incompatible_msrv, which the comment attributes to a bug where the lint does not consider cfg. That is a reasonable thing to allow, and the comment naming the upstream issue is better hygiene than a bare allow.

## Installing futures-rs and awaiting two things at once

The README gives the dependency line directly. Add it to Cargo.toml:

```toml
[dependencies]
futures = "0.3"
```

The same section states that the current futures requires Rust 1.71 or later. If your toolchain is older than that, the build will fail before any of your own code is compiled, so check the version first rather than debugging an error inside the crate.

For a bare metal target the README gives the no_std form, which turns off default features:

```toml
[dependencies]
futures = { version = "0.3", default-features = false }
```

The README is explicit that this comes with a trade-off: futures-rs works without the standard library, but "it has a significantly reduced API surface." Expect to lose parts of the combinator set, not just a flag or two.

With the dependency in place, the utilities the README names are the first thing to try. join! waits for several futures and gives you all their outputs; select! waits for whichever finishes first. The examples/functional and examples/imperative directories in the repository are where the project keeps worked code, so read those rather than guessing at the exact call shapes.

One thing to be clear about: adding the futures crate does not give you a running async program. You still need something to drive the top-level future. futures-executor is in the workspace for that, and the README does not present it as a full runtime.

## Where futures-rs stops being the right tool

The crate is a foundation, not a runtime. If you need timers, a reactor, task spawning with a work-stealing scheduler, or async I/O against real sockets, the README does not claim to provide them, and the workspace member list does not include a networking crate. futures-io exists for I/O traits, which is not the same thing as an I/O driver.

The no_std story is the second boundary. The README says the API surface is significantly reduced without std, but it does not enumerate what disappears. If you are targeting bare metal, budget time to find out which combinator you were counting on is gone, because the README will not tell you in advance.

There is also a version trap. The README's example pins "0.3", which resolves to the newest 0.3.x. The release history shows 0.3.34 on 2026-08-11, 0.3.33 on 2026-07-18 and 0.3.32 on 2026-02-15. A lockfile written months ago may still be on 0.3.32, and the gap between 0.3.32 and 0.3.34 spans roughly six months of changes. Whether any of those changes affect you is something only the CHANGELOG.md in the repository can answer; the README does not discuss upgrades at all.

## futures-rs versus tokio: two layers, not two products

The comparison people search for is futures-rs against tokio, and the honest framing is that they occupy different layers. futures-rs supplies traits and combinators. tokio supplies a runtime: a scheduler, a timer wheel, and an I/O reactor. You can use futures-rs combinators inside a tokio program, and many do, because tokio's own combinator set is smaller.

The real difference in approach shows up in what each one owns. futures-rs owns the Stream trait definition and the join!/select! macros; the README lists exactly those as its utilities. A runtime owns the thing that polls your top-level future and decides which task runs next. That is why the futures-executor crate in this workspace is a modest piece rather than a full scheduler, and why the README never presents futures-rs as a replacement for a runtime.

The practical consequence: choosing futures-rs is not an alternative to choosing tokio. It is a decision about which combinator vocabulary your codebase speaks. If your project is already all-in on one runtime's macros, adding futures-rs gives you a second set of names for the same operations, and reviewers will have to know both.

## Maintenance, licensing and what a version bump costs

The repository is not archived. The last push to main was on 2026-09-08, and the most recent release, 0.3.34, was published on 2026-08-11. Releases in the 0.3 line have been frequent: 0.3.32 in February 2026, 0.3.33 in July, 0.3.34 in August.

Upgrade cost is hard to estimate from the README, because the README does not document a migration path or a rollback procedure. The CHANGELOG.md at the repository root is the file that would carry that information; the README points to it by its presence in the tree, not by reference. The 0.3.x series has stayed on the same major and minor, so the version string in Cargo.toml does not change, but that also means a cargo update can move you across several patch releases without any visible signal in your manifest.

On licensing, the README states the crate is licensed under either the Apache License, Version 2.0 or the MIT license, at your option. The repository carries both LICENSE-APACHE and LICENSE-MIT at the top level, and the README notes that contributions are dual licensed the same way unless stated otherwise. That dual arrangement is the common Rust convention and is generally permissive, but whether it fits a particular product is a question for your own legal review, not something the README settles.

## Conclusion

Adopt futures-rs when you need Stream, join!, select! or a small executor and you are not already committed to a runtime's own combinator set. Skip it if tokio or async-std already supplies everything your code touches, since mixing two combinator vocabularies in one crate is a maintenance cost with no payoff. Before adding it, check three things: that your toolchain is Rust 1.71 or later, that the features you enable match your target (default-features = false for bare metal), and that the version you pin is 0.3.34, released on 2026-08-11, rather than an older 0.3.x. The last push to main was on 2026-09-08.

## FAQ

### Are Rust futures lazy?

The futures-rs README does not use the word lazy. What it does say is that the crate provides the foundations for asynchronous programming, including the Future trait machinery that async/await builds on, and that you need something to drive those futures to completion. The README does not spell out the polling semantics in detail; the documentation linked from the README is the place to confirm them.

### How do futures work in Rust?

futures-rs describes itself as providing the foundations, with key trait definitions like Stream plus utilities such as join!, select! and various combinator methods. Those utilities let you express asynchronous control flow, for example waiting on several futures at once or taking whichever finishes first. The README does not walk through the polling model itself.

### What is the futures-rs crate used for?

It supplies the trait and combinator layer for async Rust: Stream, join!, select! and futures combinator methods, as listed in the README. The workspace is split into crates such as futures-core, futures-util, futures-executor and futures-io, so you can depend on a narrower piece instead of the umbrella crate.

### Does futures-rs work without the standard library?

Yes. The README shows a no_std dependency line using default-features = false, and states that futures-rs works in bare metal environments. It also warns that the API surface is significantly reduced in that configuration, though it does not list which parts are removed.

### What Rust version does futures-rs require?

The README states that the current futures requires Rust 1.71 or later. The workspace Cargo.toml also allows the incompatible_msrv clippy lint, with a comment explaining that the lint does not consider cfg, which is worth knowing if you rely on that lint elsewhere.

### Is futures-rs the same as tokio?

No. futures-rs provides traits and combinators, while tokio provides a runtime with scheduling and I/O. The README presents futures-rs as foundations and lists join!, select! and combinator methods as its utilities, and the workspace includes futures-executor rather than a full scheduler.

## Sources

- [License: Apache-2.0](https://github.com/rust-lang/futures-rs/blob/main/LICENSE)
- [Project website](https://rust-lang.github.io/futures-rs/)
- [README](https://github.com/rust-lang/futures-rs/blob/main/README.md)
- [Releases](https://github.com/rust-lang/futures-rs/releases)
- [rust-lang/futures-rs on GitHub](https://github.com/rust-lang/futures-rs)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rust-lang-futures-rs
