Skip to content
214 changes: 214 additions & 0 deletions text/3485-feature-documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
- Feature Name: feature-documentation
- Start Date: 2023-09-09
- RFC PR: [rust-lang/rfcs#3485](https://github.com/rust-lang/rfcs/pull/3485)
- Rust Issue:
[rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

# Summary

[summary]: #summary

This RFC describes a new key under `features` in `Cargo.toml` for documentation.
This will allow Cargo to display this information to the user and provide a way
for `rustdoc` to eventually render this data (how this is rendered is outside
the scope of this RFC).

Example:

```toml
[features.serde]
enables = []
doc = "enable support for serialization and deserialization via serde"
```

Please see the parent meta RFC for background information: [`feature-metadata`].

# Motivation

[motivation]: #motivation

Cargo features have become extremely widely used, with many crates having at
least some level of configuration and larger crates winding up with tens of
gates. Despite being a first class component of crate structure, they suffer
from a documentation problem: users need to maintain documentation separate from
feature definition, typically a manually-created table within API docs.

This RFC proposes adding feature documentation to `Cargo.toml`, which will allow
for keeping feature definitions and documentation together.

# Guide-level explanation

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

A new `doc` key will be allowed within a feature's table. This key provides a
Copy link
Contributor

Choose a reason for hiding this comment

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

Naming (not finding the past discussion on it)

  • doc mirrors #[doc(...)]
  • description mirrors package.description
  • documentation mirrors package.documentation (but that is a URL)

We also tend to not use abbreviations as much. For example, for public-private dependencies, we discussed using pub vs public and went with public

Copy link
Member

Choose a reason for hiding this comment

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

@epage In my opinion, I think this should be doc.

I think we should encourage using this not just for a "description" of a feature but for "documentation" for a feature, and that's supported by the idea of it showing up in rustdoc and allowing markdown.

And I think there's a big difference in length between doc and documentation, compared to the difference between pub and public. (I personally would have gone with pub for that too, based on widespread usage of pub within Rust, but I also think public was less obtrusive because it's fewer characters.)

It's hard enough to get people to write documentation; let's not have any extra friction.

Copy link
Contributor

Choose a reason for hiding this comment

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

(I personally would have gone with pub for that too, based on widespread usage of pub within Rust, but I also think public was less obtrusive because it's fewer characters.)

Personally, I'd prefer we be consistent and not just focusing on character count.

It's hard enough to get people to write documentation; let's not have any extra friction.

We've shot well past that by requiring a "long form" just to write documentation, hurting both discoverability, character count, and ease of not typing some of those characters on international keyboards.

Copy link
Member

Choose a reason for hiding this comment

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

Given the #[doc] attribute is already well known for documenting stuff in markdown format, IHOM doc has best consistency and less surprising. The rustdoc doc also provides writing style guide for #[doc] (doc comment in general to be honest), so feature doc could just follow.

markdown docstring describing the feature. The first paragraph will be treated
as a summary, and should be suitable to display standalone without the rest of
the description.

Don't include the name of the feature in the summary; tools will typically
already display this documentation alongside the name of the feature.

```toml
[features]
# Feature without documentation
foo = []

# Short documentation comment
bar = { enables = ["foo"], doc = "simple docstring here"}

# Tables are preferred for longer descriptions
[features.corge]
enables = ["bar", "baz"]
doc = """
Copy link
Member

Choose a reason for hiding this comment

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

Another good question raised was "should we also support intra-doc links in this documentation?". I personally think we should and make the context the same as the crate top-level. What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

Ah I see you mentioned it below, my bad.

Copy link
Member

@weihanglo weihanglo Jun 22, 2024

Choose a reason for hiding this comment

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

Thanks for the response!

The follow-up question from me would be: Is there any compatibility issues if we hadn't implemented this RFC and rustdoc change all together? If not then this RFC can safely go first.

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure. From cargo perspective, whether there are intra-doc links or not in this documentation doesn't matter. But it'll definitely need to be mentioned when support will be discussed in rustdoc.

For the current case, I think cargo should mention that intra-doc links may be supported when rustdoc support this option and that's it. What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good to me.

@tgross35 could you add something like this? (or whichever way you'd like to rephase this)

- Rustdoc can build on this to show feature documentation.
+ Rustdoc can build on this to show feature documentation.
+ If this RFC gets stabilized before any corresponding change in rustdoc,
+ its documentation should highlight that rustdoc may parse the description and support intra-doc links in the feature.
+ Users need to be aware of this potential incompatibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added, thank you!

The first paragraph is a short summary, which might be displayed standalone.
This could be a longer description of this feature
"""
```

See [`feature-metadata`] for information about `enables`.

# Reference-level explanation

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

The new `doc` key accepts markdown-flavored text, and should be thought of as
the equivalent to a `#[doc(...)]` attribute. Like doc comments, the first line
should be treated as a summary. Intra-doc link support is not included in this
RFC, so they should not be used.

There is nothing in this RFC that cargo **must** do with this action, since it
is mainly intended for the consumption of `rustdoc` or `docs.rs`. However, it
can be used for general diagnostic information such as during `cargo add` or a
possible `cargo info` command. A sample application with `cargo add`:

```text
crab@rust foobar % cargo add regex
Updating crates.io index
Adding regex v1.7.3 to dependencies.
Features:
+ perf Enables all performance related features
+ perf-dfa Enables the use of a lazy DFA for matching
+ perf-inline Enables the use of aggressive inlining inside
match routines
+ perf-literal Enables the use of literal optimizations for
speeding up matches
+ std When enabled, this will cause regex to use the
standard library
+ unicode Enables all Unicode features

Updating crates.io index
```

_(features like `aho-corasick`, `memchr`, or `use_std` would likely be
`public = false` since they aren't listed on the crate landing page)_

Any tools that want the information in `doc` will require access to the
manifest. Adding this information to the index was decided against due to
concerns about bloat, but this is further discussed in
[future possibilities][future-possibilities].
Comment on lines +105 to +108
Copy link
Contributor

Choose a reason for hiding this comment

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

@Turbo87 why did you add crates.io team to this RFC? Without Index support, this seems like the Cargo team owns the decision making on this while input for how crates.io may want to use this in their UX would still be welcome

Copy link
Member

@Turbo87 Turbo87 Sep 8, 2025

Choose a reason for hiding this comment

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

because we were already left out of the discussion in #3416 and now we have to deal with a situation where old cargo won't be able to read newer crates on crates.io that use the new syntax. for now cargo could still normalize the new to the old syntax, but once we add additional fields (as proposed by this RFC) the normalization won't work anymore without losing that information when uploading it to crates.io.

IMHO it makes sense to include all of the relevant teams in RFCs such as this. if it turns out that our part is only small then it should be easy for our team to approve quickly.

Copy link
Contributor

Choose a reason for hiding this comment

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

Being tagged is not about being part of the conversation but about who owns the decision which the Cargo team owns the manifest format.

Copy link
Member

Choose a reason for hiding this comment

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

and yet the Cargo.toml file changes need to be supported by crates.io, so our team would very much prefer to be kept in the loop on such changes


# Drawbacks

[drawbacks]: #drawbacks

- Added complexity to Cargo.
- Exact implementation details do add test surface area
- A markdown parser is required to properly parse the `doc` field.
- Docstrings can be lengthy, adding noise to `Cargo.toml`. This could
potentially be solved with the below mentioned `doc-file` key.
- When rendering features in documentation, this RFC does not specify any way
for `rustdoc` to get the information it requires. This will require separate
design work.
- Unlike with the
[`document-features`](https://crates.io/crates/document-features) crate there
is no way to group features into sections or have a user-specified layout
- Users cannot control features ordering in documentation since the TOML
specification defines table keys as unordered.

# Rationale and alternatives

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

- To avoid increasing the size of the registry index, this does not add `doc` to
a package's index entry. This means a `.crate` file must be downloaded and
extracted to access the features.
- Feature descriptions could be specified somewhere in Rust source files. This
has the downside of creating multiple sources of truth on features.
- Cargo could parse doc comments in `Cargo.toml`, like the `document-features`
crate (linked below).

```toml
# RFC proposal
foo = { enables = [], doc = "foo feature" }

# Alternative equivalent using doc comments
## foo feature
foo = []
```

This was decided against as part of this RFC because it would mean that
TOML-compliant parsers (including anything `serde`-based) would be
insufficient to extract all information in the manifest, requiring custom
deserialization of the fields via a format-preserving parser. This differs
from documentation in Rust source as the doc-comment behavior is described
specified within the grammar with parsers supporting extracting those
elements.

# Prior art

[prior-art]: #prior-art

- There is an existing crate that uses TOML comments to create a features table:
<https://docs.rs/document-features/latest/document_features/>
- `docs.rs` displays a feature table, but it is fairly limited. If features
start with `_`, they are hidden from this table
([example](https://docs.rs/crate/regex/latest/features)).
- `lib.rs` extracts feature documentation from `Cargo.toml` and source
([example](https://lib.rs/crates/regex/features))

# Unresolved questions
Copy link
Contributor

@epage epage Sep 8, 2025

Choose a reason for hiding this comment

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

From @weihanglo at #3485 (comment)

@rfcbot concern cargo-metadata

During RustConf 2025 UnConf this RFC has been brought up again.

I wonder whether cargo metadata integration is really a hard blocker.

  • Without the integration, tools may need to use cargo-util-schemas to get the value of feature doc, though that is not too terrible.

  • Stabilizing the new manifest field itself won't block any future compatibility of cargo metadadta schema, as the feature doc field schema is pretty much settled.

  • cargo metadata JSON schema is a broader topic than feature description/documentation. If needed, we should break it out to its own issue or RFC.

Copy link
Contributor

Choose a reason for hiding this comment

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

From @kornelski at #3485 (comment)

The cargo_metadata crate could add support for the new layout easily. It could even translate the new layout to old structs (serde has a try_from indirection), so gracefully-degraded support can be added without a semver-major bump, easing update across the ecosystem.

Users parsing JSON directly will be affected only if they parse the features. Projects that only need a subset of the information (like workspace root or package IDs) wouldn't need to define serde structs for features, so they're likely to ignore changes to the features.

So from compatibility perspective, I think Cargo could get away with simply mirroring the features layout from Cargo.toml in the metadata JSON. It's not a bigger deal than changing Cargo.toml itself which also requires tools to update their parsers (I've already made my cargo_toml crate transparently translate the new layout to the old format).

An interesting alternative would be if cargo metadata --format-version=2 only used the new layout, even for crates that use the old one in Cargo.toml. This would make life easier for consumers of the metadata who would need to parse only one layout.

Having both layouts in parallel in the JSON (adding features2 or such) seems least attractive to me. It wouldn't save Cargo the hassle of converting feature layouts for backwards compatibility, but it would add bloat to the JSON. For tools, parsing the metadata is often on the critical path (discovering the workspace to operate on), so smaller JSON is better.

Even if it's convenient now to add a parallel features2 key to the v1 JSON, when v2 happens eventually, I assume Cargo would make it an opportunity to clean up the features layout. So long-term Cargo would end up supporting the two alternative layouts anyway, and taking a shortcut now won't avoid the work long term.

Copy link
Contributor

@epage epage Sep 8, 2025

Choose a reason for hiding this comment

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

From @weihanglo

I wonder whether cargo metadata integration is really a hard blocker.

To be clear, I didn't say it is a blocker, but we need to decide whether it should be a blocker. At this moment, the RFC does not acknowledge the impact on cargo metadata, even in putting it as a future possibility. You are correct that no matter what we do, it will be additive so it can always be added in the future. The question is whether this feature is "complete enough" with it or would it be "half baked".

I'd also add a blocking question of "do we feel comfortable with the options for adding cargo metadata" because that could provide feedback back onto this RFC.

Copy link
Contributor

Choose a reason for hiding this comment

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

From @kornelski

For myself, I was a bit confused as to what options you were speaking to so let me check if I synthesized your whole post correctly:

For the packages that use the new capabilities, features would have a new layout. Since its conditioned on people using it, its not technically a breaking change though it can be disruptive. That disruption can be reduced by cargo_metadata being updated to gloss over it for people (requiring an update through a major version). On --format-version 2, cargo metadata could switch to exclusively using the new layout.

Having both layouts in parallel in the JSON (adding features2 or such) seems least attractive to me. It wouldn't save Cargo the hassle of converting feature layouts for backwards compatibility, but it would add bloat to the JSON. For tools, parsing the metadata is often on the critical path (discovering the workspace to operate on), so smaller JSON is better.

Even if it's convenient now to add a parallel features2 key to the v1 JSON, when v2 happens eventually, I assume Cargo would make it an opportunity to clean up the features layout. So long-term Cargo would end up supporting the two alternative layouts anyway, and taking a shortcut now won't avoid the work long term.

From what I gather, "avoiding bloat" is not a high priority for cargo metadata. We'd likely be better served by changing serialization formats or fundamentally restructuring how we do plumbing commands

What this misses out on is that we would be less disruptive to callers of cargo metadata by having a features2. Tools could look at contents of crates without needing to be updated to handle new package versions.

Copy link
Contributor

Choose a reason for hiding this comment

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

@weihanglo looking at @kornelski's proposal, that is an example of something that we'd need to decide before stabilization. If we make the layout of features dynamic, based on whether the new capabilities are used or not, then we have to do that from the initial release or else its a breaking change.


[unresolved-questions]: #unresolved-questions

- Rather than being consistent with `rustdoc` and accepting markdown, should the
`doc` key be consistent with `package.description` and only support plain
Comment on lines +173 to +174
Copy link
Contributor

Choose a reason for hiding this comment

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

Wanted to highlight this for discussion. My main interest is in being able to show summaries in cargo add. Might be good to reach out to @kornelski for what they have seen of how features are documented through the ecosystem as that might help show potential requirements.

Copy link
Contributor

Choose a reason for hiding this comment

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

Package descriptions tend to use markdown `code` and *emphasis*. Rust devs really like using ` everywhere. Even rustc uses ` in terminal error messages.

Markdown's goal is to look fine even when displayed as plain text.

You could define it as the first line being for CLI help, and the rest for docs. Analogous to how rustdoc handles doc comments.

Copy link
Member

Choose a reason for hiding this comment

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

@kornelski 👍 for using markdown. And I think it makes sense to treat the first logical line (anything before the first double-newline) as a "short description", cutting off subsequent paragraphs in places where full documentation doesn't fit.

text? This needs to be a point of discussion before approval of this RFC.

# Future possibilities

[future-possibilities]: #future-possibilities

- Rustdoc can build on this to show feature documentation.

If this RFC gets stabilized before any corresponding change in rustdoc, its
documentation should highlight that rustdoc may parse the description and
support intra-doc links in the future, but not at the current time. Users need
to be aware of this potential incompatibility.
- At some point, the decision to not include `doc` in the index could be
reevaluated. Including only the first (summary) line of `doc` could be a
possibility.
- `cargo add` can show the `doc` and `deprecated` summary with the listed
features.
- [`cargo-info`] can use this information to provide feature descriptions.
- crates-io could be updated to render feature documentation
- Feature documentation could be allowed in a separate markdown file. For
convenience, markdown anchors could be used to specify a section, so multiple
features can share the same file. This could be a good option for features
requiring long descriptions.

```toml
foo = { enables = [], doc-file = "features.md#foo" }
bar = { enables = [], doc-file = "features.md#bar" }
```

[cargo #12335]: https://github.com/rust-lang/cargo/issues/12235
[cargo #10882]: https://github.com/rust-lang/cargo/issues/10882
[`cargo-info`]: https://github.com/rust-lang/cargo/issues/948
[`deprecated`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute
[`deprecated-suggestions`]: https://github.com/rust-lang/rust/issues/94785
[discussion on since]: https://github.com/rust-lang/rfcs/pull/3416#discussion_r1172895497
[`public_private_dependencies`]: https://rust-lang.github.io/rfcs/1977-public-private-dependencies.html
[`rustdoc-cargo-configuration`]: https://github.com/rust-lang/rfcs/pull/3421
[`tokio`]: https://docs.rs/crate/tokio/latest/features
[visibility attribute]: https://ant.apache.org/ivy/history/latest-milestone/ivyfile/conf.html
[`feature-metadata`]: https://github.com/rust-lang/rfcs/pull/3416