futures-core semver

Purpose of this document

This is a Collaborative Summary Document for futures-rs#2362. The goal is to get a shared understanding of the facts on the ground, the options for resolution, and the reasons we might choose one option over the other.

How to contribute to this document

Please feel free to leave comments with suggestions for edits. For the time being, you can also edit directly-- however, it’s good practice to leave a comment if you do so requesting comment on the changes. Whether you make or propose a change, please keep in mind that the goal is to refine the document into something factual that everyone can agree with (this doesn’t mean that opinions or values can’t be expressed, just that they should be clearly labeled as such; this converts them into facts). If you need help on how to phrase something, ping nikomatsakis to talk it over.

Basic background

  • The futures crate is a facade of a number of other crates. The intent is that the semver numbers for these other crates can be incremented only with due consideration, while permitting rapid iteration for futures and its utilities:
  • futures-core is a, well, core crate, and hence one whose semver should be incremented carefully. In particular, it contains traits that are likely to appear in the public API of other crates.
  • The futures facade and futures-util crates are meant to be “private details” and hence their semver can be incremented more easily.
  • The stream trait has existed in futures-core 0.3 for a long time (rustdoc); let’s refer to this trait as Stream:0.3
  • async-std 1.9 re-exports (via pub use) the Stream:0.3 trait as part of its public API; perhaps other crates do too
  • The stream trait is being added to libstd (link? PR?), we call call this Stream:std
  • In its current form, it is identical to the trait from futures-core 0.3
  • futures-core 0.3.13 contains the following main things
  • the future module, which re-exports core::Future
  • the stream module, which defines the Stream:0.3 trait
  • the task module, which re-rexports core::Task
  • the ready! macro
  • futures-util 0.3.12 depends on futures-core 0.3.13 and includes a StreamExt trait, which we will call StreamExt:0.3.12
  • this trait includes a method next along with other useful combinators
  • the RFC for Stream:std initially proposed including next in the Stream:std trait
  • some would like to move other combinators onto Stream:std over time as well, for ergonomic reasons
  • There are plans for a Futures 1.0 release which will include various changes, along with increments to various version numbers. Relevant to this doc:
  • futures-core is the subject of this doc
  • futures-util 0.3 → 1.0
  • Everyone wants Futures 1.0 to re-export the Stream:std trait, but there are two ways to do it:
  • Option A: Release a futures-core 0.3.14 which replaces Stream:0.3 with a re-export of Stream:std
  • Option B: Release a futures-core 0.4 which re-exports Stream:std

Implications of Option A (releasing futures-core 0.3.14)

  • futures-core 0.3.14 could not be released until the Stream:std trait is stabilized, or else futures-core would require nightly support, which would be a breaking change
  • Nobody feels time pressure here, though, so this is not an issue.
  • Scenario A.1: methods added before futures 1.0 is released
  • The next method is added to Stream:std in Rust release
  • Later, Futures 1.0 is released, including futures-core 0.3.14 
  • Result: Users of futures-util 0.3 who bump their Cargo.lock to futures-core 0.3.14 (which is a minor semver version) will encounter an ambiguity for calls to next
  • Ambiguity between: method defined on the core trait and the StreamExt version
  • Not technically a violation of our semver rules, but a potential problem in practice
  • Scenario A.2: methods added after futures 1.0 is released
  • Futures 1.0 is released, with futures-util 1.0 that contains the StreamExt trait as is
  • Later, the next method is added to Stream:std in Rust release 1.N
  • futures-util 1.0.1 is released which removes the next method from StreamExt, perhaps conditionally (using cfg:accessible or some other means)
  • Result: Users of futures-core 0.3 will encounter ambiguity if they bump their Cargo.lock and are using rust release 1.N
  • Result: Users of  futures-util 1.0 will encounter ambiguity as described in A.1 when they upgrade to rust release 1.N

Implications of Option B (releasing futures-core 0.4)

  • crates which re-export Stream:0.3 would have to move to futures-core 0.4, which is a breaking change
  • for example, this would require the release of async-std 2.0
  • this would have trickle-down effects throughout the ecosystem, since downstream crates that rely on async-std would have to upgrade to 2.0
  • this is primarily an issue for “middleware” or “library” crates, not so much for “application” level crates
  • Scenario B.1: methods added before futures 1.0 is released