diff --git a/text/3487-feature-visibility.md b/text/3487-feature-visibility.md new file mode 100644 index 00000000000..d57c243df02 --- /dev/null +++ b/text/3487-feature-visibility.md @@ -0,0 +1,165 @@ +- Feature Name: feature-metadata +- Start Date: 2023-09-08 +- RFC PR: [rust-lang/rfcs#3487](https://github.com/rust-lang/rfcs/pull/3487) +- 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` to indicate that a +feature is private. + +Please see the parent meta RFC for background information: [`feature-metadata`]. + +# Motivation + +[motivation]: #motivation + +[Cargo features](link to features doc) are one of the main means to support conditional build and optional dependency +configuration in Rust crates. Often these are for configuration options that a +library user may want, but another common use case is hiding API that shouldn't +be available to downstream users. Examples include: + +- Debugging, benchmarking or test-related features that expose unstable internal + API +- Intermediate features that are enabled by user-facing features but not meant + to be used on their own (e.g. a feature enabling dependency features) + +A way to hide these features from user-facing configuration will make options +easier to understand and lowers the chance of library users accidentally using +unstable internal API. + +# Guide-level explanation + +[guide-level-explanation]: #guide-level-explanation + +There will be a new flag allowed within `[features]`: `public`. This is boolean +flag defaulting to `true` that indicates whether or not downstream crates should +be allowed to use this feature. + +```toml +[features] +foo = { enables = [], public = false} +``` + +Attempting to use a private feature on a downstream crate will result in +messages like the following: + +``` +error: feature `baz` on crate `mycrate` is private and cannot be used by + downstream crates +``` + +# Reference-level explanation + +[reference-level-explanation]: #reference-level-explanation + +`public` is a boolean value that defaults to `true`. It can be thought of as +`pub` in Rust source files, with the exception of being true by default. If set +to `false`, Cargo should forbid its use with an error message on any downstream +crates. + +The default `true` is not consistent with [`public_private_dependencies`] or +Rust's `pub`, but is a reasonable default to be consistent with the current +behavior. This means that either `feature = []` or +`feature = { "enables" = [] }` will result in the same configuration. + +The name `public` was chosen in favor of `pub` to be consistent with the +[`public_private_dependencies`] RFC, and to match the existing style of using +non-truncated words as keys. + +In general, marking a feature `public = false` should make tooling treat the +feature as non-public API. This is described as the following: + +- The feature is always usable within the same crate: + - Enabled by other features, e.g. + `foo = { enables = [some-private-feature] }`, is allowed + - Referenced in `[[bench]]` and `[[test]]` target `required-features` + - Using the feature on the command-line is allowed +- Users may explicitly specifying the private features for their dependencies + on the command-line (e.g. `--features somecrate/private-feature`) which would + otherwise be forbidden +- The feature should not be accepted by `cargo add --features` +- The feature should not be reported from `cargo add`'s feature output report + - A future tool like `cargo info` shouldn't display information about these + features +- Once `rustdoc` is able to consume feature metadata, `rustdoc` should not + document these features unless `--document-private-items` is specified + +Attempting to use a private feature in any of the forbidden cases should result +in an error. Exact details of how features work will likely be refined during +implementation and experimentation. + +This feature requires adjustments to the index for full support. This RFC +proposes that it would be acceptable for the first implementation to simply +strip private features from the manifest; this means that there will be no way +to `cfg` based on these features. + +Full support does not need to happen immediately, since it will require this +information be present in the index. The [`feature-deprecation`] RFC describes +a way to add attributes to features in a forward-compatible way under a +`features3` key, which would be suitible for any additional information needed +here. + +# Drawbacks + +[drawbacks]: #drawbacks + +- Added complexity to Cargo. Parsing is trivial, but exact implementation + details do add test surface area +- Added Cargo arguments if escape hatches for `public` are created +- This adds confusion to the `cfg` diagnostics introduced in + + +# Rationale and alternatives + +[rationale-and-alternatives]: #rationale-and-alternatives + +- Currently, `docs.rs` will hide features from its autogenerated feature list + if they start with a leading underscore. This convention would work here, but + it would not be consistent with the Rust language (leading underscores indicate + unused variables, lang items are used to indicate visibility) + +# Prior art + +[prior-art]: #prior-art + +- `docs.rs` treats features with a leading `_` as private / hidden +- Ivy has a [visibility attribute] for its configuration (mentioned in + [cargo #10882]) +- Discussion on stable/unstable/nightly-only features + + +# Unresolved questions + +[unresolved-questions]: #unresolved-questions + +- Are the semantics of `public` proposed in this RFC suitable? Should private + features be usable in examples or integration tests without a `--features` + argument? +- Does `public` need to be in the index? + +# Future possibilities + +[future-possibilities]: #future-possibilities + +- A `stable` field can be set false to indicate API-unstable or nightly-only + features (something such as `stable = 3.2` could be used to indicate when a + feature was stabilized). See also: + +- The `public` option could be used to allow optional dev dependencies. See: + + +[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-deprecation`]: https://github.com/rust-lang/rfcs/pull/3486 \ No newline at end of file