Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions text/0000-stable_since.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
- Feature Name: stable_since
- Start Date: 2025-09-12
- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)
- Rust Issue: [rust-lang/rust#74182](https://github.com/rust-lang/rust/issues/74182)

# Summary
[summary]: #summary

Allow crates to specify `#[stable(since = "version")]` on items for Rustdoc to render the information.

# Motivation
[motivation]: #motivation

This functionality is already implemented for the standard library. Other crates also expand their public API over time, and it would be helpful to inform users about the minimum crate version required for each item.

It's possible to automatically infer in which version each item has been added by comparing public APIs of crates' public releases, but this is too expensive and complicated to perform each time when generating documentation. The `#[stable(since)]` attribute behaves like a first-class cache for this information for `rustdoc` and other tools.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

When added to an item, it specifies in which version of the crate this item has been added:

```rust
#[stable(since = "2.25.0")]
pub fn add_trombone_emoji() {}
```

Rustdoc will include the version in the documentation of the item, with a description such as "stable since $crate_name version 2.25.0".

To ease development of unreleased features, there is no restriction on the version range, and it may refer to a future not-yet-released version.

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

The `version` must parse as SemVer. It refers to a version of the crate that defines the item this attribute belongs to.

`rustc` doesn't need to be made aware of crates' versions, because there's no restriction on the version range.

`rustdoc` should not display the attribute on items re-exported from other crates.

# Drawbacks
[drawbacks]: #drawbacks

This attribute may be incorrect if added manually. It may be confused with rustc version compatibility.

It specifies only a single version per item, which may not be enough to fully explain availability of items that are available conditionally or under different paths.

Versions on re-exported items are not relevant for the crate re-exporting them, because it matters when the re-export has been added.

# Rationale and alternatives
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be multiple ways something can be stable. Today, std also uses const_stable. I assume this should at least be acknowledged in case it can affect the design. Today they are separate attributes. Should we instead generalize stable to make it work for both? What impact may that have on this attribute?

[rationale-and-alternatives]: #rationale-and-alternatives

- The entire `#[stable(feature)]`/`#[unstable(feature)]` functionality could be stabilized for 3rd party crates
- API stability could be stored outside of the source code, e.g. in a file similar to `rustdoc`'s JSON
- It could be shortened to `#[since("version")]`
- It could be expanded to `#[stable(added = "version", changed = "version", rust_version = "msrv")]`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Does "msrv" really belong here? If the rustc version is too old the crate would simply refuse to be compiled, regardless if the you the tagged API or not. Besides you can't bump the MSRV without publishing the package, so it can be inferred from the changed crate version already.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could see the argument that a person would maybe want to know "if I use this then I can't go back in rust version farther then X", but that's extremely rare. Usually people seem to have a particular (possibly locked) version of rust available, and then want to know what they can do while on that same version of rust.


# Prior art
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also talk about #[deprecated] as it serves a similar role.

[prior-art]: #prior-art

The `#[stable(since = "version")]` attribute is a subset of standard library's `#[stable(feature, since)]`, but to keep the scope small, this RFC does not include support for feature flags nor unstable APIs outside of the standard library.

# Unresolved questions
[unresolved-questions]: #unresolved-questions

Should crates reset the `version` when making semver-breaking changes to the item?

Should the `version` allow a placeholder value like `UNRELEASED`?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a rule like "you must put a string that can be a cargo crate version" is a simple enough rule, and however people want to get fancy with it is up to the ecosystem. For example, people could put the next minor version to be released, or put 0.0.0 as a placeholder, or whatever fits the project's tools and workflow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is indeed, but if people are going to use 999.999.999 or 0.0.0-unreleased etc. as placeholders, we could as well provide some placeholder out of the box.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would love placeholder support as well as a teaching aid to use it (e.g. a clippy lint saying that the since is too new)

How does #[deprecated(since)] deal with any of this?


Is it clear enough that the version is the crate's own version and not the minimum requierd Rust version?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you thinking that a person would see a 1.0-series crate with "stable since 1.34" or something, and then think that it means that the crate provides an item as long as the rust version is 1.34? Because that's... Not How It Works for anything else in rust crates. A particular user could think that, but as soon as they ask almost anyone else I'm sure they'd be corrected.

Copy link

@hanna-kruppe hanna-kruppe Sep 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, however, that there are real world examples of crates providing their own APIs based on the Rust version (e.g., camino and fs-err wrap std APIs and use Rust version detection to support new additions without constantly bumping MSRV).


How to support items re-exported from other crates? Could `use` support overriding `#[stable(since)]`?

# Future possibilities
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cargo update could tell people to run some new command, like cargo report news clap@before clap@after and see what is newly available.

Even better if we can get some form of "behavior change" and "remove attributes" (along with the stable deprecated) along with build-flow analysis and tailor those parts of the report specifically to your use of the library (plus the new stuff)

[future-possibilities]: #future-possibilities

The entire `#[stable(feature)]`/`#[unstable(feature)]` functionality could be stabilized for 3rd party crates.

Tools like rust-analyzer or clippy could help users bump versions in `Cargo.toml` when their crate uses items from a newer version of a dependency than the minimum version specified in `Cargo.toml`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love to have something like clippy::incompatible_msrv for dependencies. We'd have to tell the "compiler" (clippy in this case) the minimum version of every direct dependency (and maybe their pub dependencies)


These attributes could be automatically generated by tools like `cargo-public-api` or `cargo-semver-checks`.