Rust feature staging and you
Recently I've been overhauling the mechanisms for introducing unstable
features to both the Rust language and libraries. Part of that work
landed for the 1.0.0-alpha release when we started producing warnings
about using unstable library APIs, which I'm sure you noticed. Now the
transition to the system described in RFC 507 is nearing
completion, so it's time to remind everybody what's happening and how
to cope with it.
We want to evolve the Rust process such that new features progress
into the language in a controlled and predictable way, so that there
are yet fewer suprizes about what is or isn't ready for use, and risk
of new features becoming de-facto stable through early adoption is
minimized. From now on every feature going into either the Rust
language or the standard library is identified by a feature name,
extending the feature-gating mechanism used by the language to the
standard library.
For the remainder of the current development cycle, the nightly
compiler will print a warning for all code that uses unstable features:
/home/brian/test.rs:1:22: 1:44 warning: use of unstable library feature 'core'
/home/brian/test.rs:1 fn main() { unsafe { std::intrinsics::abort() } }
^~~~~~~~~~~~~~~~~~~~~~
/home/brian/test.rs:1:22: 1:44 help: add #![feature(core)] to the crate attributes to silence this warning
/home/brian/test.rs:1 fn main() { unsafe { std::intrinsics::abort() } }
^~~~~~~~~~~~~~~~~~~~~~
During the first 1.0 beta cycle these will be converted to errors on
the nightly channel, and authors will be required to explicitly opt-in
to use of unstable libraries to keep their code compiling. On the beta
and stable channels it will not be possible to use unstable
features. We hope that between upstream stabilization and feedback
from downstream authors we will converge upon a clearly-defined subset
of the Rust libraries that are stable and that can support a
substantial collection of the crates in the Cargo registry.
Feature staging for crate authors
Taking Rust from its historical posture in which unstable features are
always available to one in which only stable features are available in
production releases is a big and painful change to work through. As a
crate author here's what you need to know:
To use unstable features you must always declare so by putting
#![feature(foo)]
at the top of your crate. While this has been true
for language features for a long time, it is now true for the standard
library as well. You are going to be doing a lot more of this in the
short term until std stability for 1.0 has completely shaken out -
hang in there!
Once the betas start and using unstable features without opting in
becomes an error (wheras now it's a warning), it will be trivial to
determine whether your code is using the stable - and backwards
compatible - subset of the language: any Rust crate that contains no
#![feature]
attributes is written in a stable dialect of Rust.
As introduced, the current unstable library features are
rather-coarse-grained. Some of those likely to impact existing code
include:
-
io
- All ofstd::io
-
os
- All ofstd::os
-
path
- All ofstd::path
-
hash
- All ofstd::hash
-
collections
- Thecollections
crate and collections reexported
fromstd
-
rand
- Therand
crate and its componentsn reexported fromstd
-
core
- Everything else that's unstable incore
-
std_misc
- Everything else that's unstable instd
-
test
- Thetest
crate -
rustc_private
- In-tree components that only the compiler should
be using
During their unstable development feature names may change
incompatibly, and in particular be divided into smaller subsets with
different unique feature names. You are likely to see more features
appear in the coming weeks as the remaining unstable APIs are
scrutinized.
In this initial revision the core
and std_misc
features cover a
wide range of functionality - they are 'catch-all' features for those
APIs that haven't been properly categorized. Being forced to enable
one of these features may indicate a 'gap' in the 1.0 stability story.
As a crate author if you see a logical subdivision of functionality
that is currently assigned the core
or std_misc
feature please
suggest the feature be created, either here or on the issue tracker.
Having more named features will help us better track library
stabilization.
If this seems painful try to be reassured that we dearly want your
crates to work on the stable release channel and will do everything we
can to make that happen as fast as reasonably possible. In the coming
weeks there will be a concerted effort to lift as much of the cargo
ecosystem onto the stable subset of the language as possible.
Deprecation continues to work the same as it always has, via the
'deprecated' lint, so deprecated APIs can be opted into with
#[allow(deprecated)]
.
Feature staging for rustc authors
For people hacking on rust itself there are a number of things to know.
Firstly, the in-tree design is not intended to make your life easier -
lots of stuff has to be tagged with metadata, but only incrementally
more so than before.
Crates that participate in feature staging (i.e. every crate in-tree
and no others) need to opt in. This requires the #[staged_api]
attribute, which itself requires the staged_api
feature. Being
'staged_api' is what tells rustc it needs to interpret the 'unstable',
'stable', and 'deprecated' attributes (while leaving the possibility
of also using those names for non-staged-api purposes later).
Therefore every crate in-tree must say:
#![feature(staged_api)]
#![staged_api]
Nearly every crate that is not part of the standard facade should
additionally include the crate header #![unstable(feature = "rustc_private")]
, which says 'this crate is not for public
consumption'.
When adding a new public API to the standard library or to a crate in
the standard facade, you need to tag it unstable and give it a feature
name:
#[unstable(feature = "new_hotness")]
fn new_hotness() { }
Increasingly, this feature name should come out of the RFC process,
and adding APIs without knowing what feature it is part of will be an
indication that the development process is not proceeding in order.
Unstable features inherit lexically, so you often only need a
single one.
When a feature becomes stable you change the attribute from 'unstable'
to 'stable' and add the 'since' attribute:
#[stable(feature = "new_hotness", since = "1.1.0")]
fn new_hotness() { }
'since' is the version in which that feature will hit the stable
release channel and at the time of promotion should be equal to the
value CFG_RELEASE_NUM
in main.mk
.
The stable
attribute does not inherit lexically, so most items need
to be tagged explicitly, including trait methods and inherent methods.
No single feature can be both unstable and stable, so if only part of
an unstable feature needs to be promoted then it must be split into
two features. All the 'since' attributes must agree for a given
feature. A new tidy script enforces both these and can be run with
'make tidy-features'.
Deprecating an API can be done with #[deprecated(since = "1.1.0")]
,
the same as before, but now requiring 'since'. Since both unstable and
stable libraries can be deprecated, all deprecated attributes must be
paired with either 'unstable' or 'stable'.