Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Evolving the standard library API across editions

Metadata
Point of contactAmanieu d’Antras
StatusProposed
Tracking issue
Zulip channelN/A
Teamscompiler, edition, lang, libs, rustdoc
Task owners(none)

Summary

Add a mechanism for edition-dependent re-exports which allows the standard library to make larger API changes across editions.

Motivation

The library team is generally very careful about stabilizing new APIs in the standard library because once such an API is stable, it must remain unchanged forever. This is necessary to maintain Rust’s stability guarantees which ensure that rustc is able to compile Rust code from 2015 and that new crates are able to interoperate with old crates.

However there exist several stable standard library APIs which we would wish to change to improve ergonomics and safety, if it were possible. While the library API team is not committed to making any specific change, several changes have previously been discussed:

The exact changes are outside the scope of this project goal and should be discussed elsewhere. This goal is only about giving us the tools needed to potentially make such changes.

The status quo

Currently the only existing tool for making such changes is deprecating the old API and introducing a new one with a similar name. For example, this is what was done with Command::pre_exec and Command::before_exec. However this approach has several downsides:

  • It can often be hard to pick a good alternate name for a function or type since the obvious names will have already been chosen for the original (stable) API. Consider the case of Atomic*::fetch_update which is being deprecated in favor of Atomic*::update and Atomic*::try_update: the original name was arguably better since it clarifies what value is returned.
  • A deprecation is a strong signal that effectively forces every still-maintained crate to update to the new API. This can cause a lot of ecosystem churn as everyone updates to the new API.
  • Deprecated types in the public API of a crate are particularly problematic because migrating to the new API would result in a breaking change which requires a major version bump.

What we propose to do about it

We would like the ability to make larger API breaking changes across editions. To do so we would like to add a mechanism for edition-dependent re-exports. This would allow a single path in the standard library to resolve to different items depending on the edition of the code that is referencing it. For example:

#![allow(unused)]
fn main() {
// std
mod foo {
    struct Bar2024;
    struct Bar2027;

    // Exact syntax for this is not definitive
    #[edition = "2024"]
    use Bar = Bar2024;
    #[edition = "2027"]
    use Bar = Bar2027;
}

// 2024 edition crate
use std::foo::Bar; // resolves to std::foo::Bar2024

// 2027 edition crate
use std::foo::Bar; // resolves to std::foo::Bar2027
}

This would allow for major changes in the standard library API while still allowing for backwards compatibility with older editions since the items used by older editions are still available, just through a different path. Additionally, automatic edition upgrade could automatically re-write paths to use the one for the old item, which ensures that code behavior doesn’t change during the automatic upgrade.

Work items over the next year

TaskOwner(s)Notes
Draft RFCAmanieu d’Antras
Implement compiler supportowner

Team asks

TeamSupport levelNotes
compilerMediumDesign discussions and implementation review.
langSmallReview of the feature and lang implications.
libsLargeDetermine what API changes should be made across editions.
editionLargeReview the feasibility of this proposal as well as the specific API changes.
rustdocMediumFigure out how such API changes should be presented in the API docs.