Stable Language Evolution

Stable vs. Unstable Features

Rust separates language syntax, standard library APIs, and compiler flags into stable features—which carry a permanent backward-compatibility guarantee (#[stable(feature = "...", since = "...")])—and unstable features (#[unstable(feature = "...", issue = "...")]), which are under active design iteration and can only be enabled on the nightly compiler channel via an explicit #![feature(...)] crate-level attribute.

+-------------------------------------------------------------------+
|                   Rust Compiler Codebase (`rustc`)                |
|                                                                   |
|  Stable Features (`#[stable(..., since = "1.85.0")]`)             |
|    ---> Available on Stable, Beta, AND Nightly compilers!         |
|    ---> Guaranteed never to break backward compatibility!         |
|                                                                   |
|  Unstable Features (`#[unstable(..., issue = "12345")]`)          |
|    ---> Blocked by feature gate on Stable & Beta (`error[E0554]`) |
|    ---> Unlocked ONLY on Nightly via `#![feature(name)]`!         |
+-------------------------------------------------------------------+

Comparing Stable, Beta, and Nightly Feature Access

Compiler Channel Accepts #![feature(...)]? Unstable CLI Flags (-Z ...)? Backward Compatibility Guarantee
Stable (rustup default stable) No (error[E0554]) No (Requires -C ... stable flags) Strong SemVer stability: valid stable code continues to compile across future 1.x releases.
Beta (rustup +beta ...) No No Pre-release candidate for the upcoming 6-week Stable release (used in CI to catch regressions early).
Nightly (rustup +nightly ...) Yes Yes (-Zsanitizer=..., -Zbuild-std) None for unstable features: #![feature(...)] APIs may be renamed, modified, or removed on any nightly build.

How the Feature Gate Rejects #![feature(...)] on Stable Rust

Why does the stable compiler explicitly forbid #![feature(...)] even though the implementation code for experimental features already sits inside the same rustc binary?

If stable compilers allowed #![feature(...)], library crates on crates.io would begin depending on half-finished experimental features, effectively freezing those experimental designs before their edge cases were solved! Blocking #![feature(...)] on stable compilers (error[E0554]) protects the ecosystem from accidental lock-in to unstable APIs:

// ❌ does not compile
#![feature(never_type)]

fn main() {
    println!("Attempting to use #![feature(...)] on a stable compiler");
}
error[E0554]: `#![feature]` may not be used on the stable release channel
 --> src/main.rs:1:1
  |
1 | #![feature(never_type)]
  | ^^^^^^^^^^^^^^^^^^^^^^^

Conditional Nightly Acceleration (cfg_attr)

When a library wants to compile 100% on stable Rust by default while optionally enabling a nightly-only optimization or documentation feature (such as doc_auto_cfg on docs.rs or portable SIMD via an opt-in Cargo feature nightly), combine #![cfg_attr(...)] with a Cargo feature flag:

// Compiles cleanly on Stable Rust!
// Only enables `#![feature(...)]` when `--features nightly-optimizations` is passed on a nightly compiler:
#![cfg_attr(feature = "nightly-optimizations", feature(test))]

pub fn compute_popcount_sum(data: &[u64]) -> u32 {
    data.iter().map(|&x| x.count_ones()).sum()
}

fn main() {
    let values = [0b1011u64, 0b1111, 0b0001];
    println!("Total set bits on stable Rust: {}", compute_popcount_sum(&values));
}
Total set bits on stable Rust: 8