Rust Error E0658: Use of Unstable Library Feature (Feature Is Experimental)
E0658 means: you used something that exists in Rust, but isn’t finished yet, so stable Rust won’t let you use it. It’s usually a method you found in the standard library docs or in a blog post, like .intersperse() or .try_collect(), or a piece of syntax like try { ... }. Your code isn’t wrong, and nothing is broken on your machine. You just reached for a feature that only works on the nightly compiler. Almost every time, there is a stable way to do the same thing in one line. (This page is part of Rust compiler errors explained.)
The error message
Section titled “The error message”error[E0658]: use of unstable library feature `iter_intersperse` --> src/main.rs:3:47 |3 | let joined: String = tags.iter().copied().intersperse(", ").collect(); // ❌ nightly-only method | ^^^^^^^^^^^ | = note: see issue #79524 <https://github.com/rust-lang/rust/issues/79524> for more informationThis is the program that produces it:
fn main() { let tags = ["rust", "web", "cli"]; let joined: String = tags.iter().copied().intersperse(", ").collect(); // ❌ nightly-only method println!("{}", joined);}What it means
Section titled “What it means”The ^^^^^^^^^^^ points at intersperse on line 3. The headline gives the feature name in backticks, iter_intersperse. That’s the name the Rust team uses for this unfinished piece of the standard library. The note: links the GitHub issue where the feature is being discussed.
So rustc found the method. It knows exactly what you mean. It just won’t compile it, because intersperse is still marked unstable: it might change its name, its arguments or its behaviour before it’s finished, or it might be removed.
Other forms of the headline mean the same thing for language features instead of library ones: the `!` type is experimental, `try` expression is experimental, or associated type defaults are unstable.
It’s like a shop that has a new product on the shelf with a “staff only, still testing” sticker. You can see it, you can read the label, but the till won’t sell it to you yet.
Why Rust does this
Section titled “Why Rust does this”Rust makes a strong promise: code that compiles on stable Rust today keeps compiling on every future stable Rust 1.x. To keep that promise, new features first land on the nightly channel, where people can try them and the design can still change. Only when a feature is done does it become stable, usually after months or years of testing.
Stable Rust refuses to compile unstable features at all, so you can’t build on something that might change under you. If you really want to try one, nightly lets you switch it on by name with #![feature(iter_intersperse)]. On stable, even that line is an error (E0554).
The standard library docs show both kinds side by side. Stable items have a version number on the right, like 1.82.0: the Rust version where they became stable. Unstable items have a box that says “🔬 This is a nightly-only experimental API.” That box is the quickest way to spot an E0658 before you write any code. See Rust Tools & Setup for how Rust’s channels and rustup fit together.
How to fix it
Section titled “How to fix it”Fix 1: Use the stable alternative
Section titled “Fix 1: Use the stable alternative”Choose this almost every time. Unstable methods are usually shortcuts for something stable Rust can already do. To glue strings together with a separator, join on a slice or a Vec does what intersperse(...).collect() would:
fn main() { let tags = ["rust", "web", "cli"]; let joined = tags.join(", "); println!("{}", joined);
// Strings made by an iterator: collect them into a Vec first, then join. let upper: Vec<String> = tags.iter().map(|t| t.to_uppercase()).collect(); println!("{}", upper.join(" | "));}Output:
rust, web, cliRUST | WEB | CLIIf you’d rather keep everything as one iterator chain, the popular itertools crate has a stable join and intersperse for iterators. See Strings and Iterators.
Fix 2: Check the docs for the “nightly-only” badge
Section titled “Fix 2: Check the docs for the “nightly-only” badge”Choose this when you copied a method from the docs, an AI answer or a blog post. Open the method in the std docs: if it has the 🔬 box, look a bit further for the stable way. A common one is .try_collect():
error[E0658]: use of unstable library feature `iterator_try_collect` --> src/main.rs:3:80 |3 | let numbers: Result<Vec<i32>, _> = inputs.iter().map(|s| s.parse::<i32>()).try_collect(); // ❌ nightly-only | ^^^^^^^^^^^ | = note: see issue #94047 <https://github.com/rust-lang/rust/issues/94047> for more informationThe broken program:
fn main() { let inputs = ["3", "14", "x"]; let numbers: Result<Vec<i32>, _> = inputs.iter().map(|s| s.parse::<i32>()).try_collect(); // ❌ nightly-only println!("{:?}", numbers);}Stable Rust already does this with plain collect(). When the items are Results and you collect into a Result<Vec<_>, _>, you get Ok with all the values, or the first Err:
use std::num::ParseIntError;
fn parse_all(inputs: &[&str]) -> Result<Vec<i32>, ParseIntError> { inputs.iter().map(|s| s.parse::<i32>()).collect()}
fn main() { println!("{:?}", parse_all(&["3", "14", "15"])); println!("{:?}", parse_all(&["3", "x", "15"]));}Output:
Ok([3, 14, 15])Err(ParseIntError { kind: InvalidDigit })Other unstable ones you may run into, with the stable route: .array_chunks() on an iterator (iter_array_chunks; use .as_chunks() or .chunks() on a slice instead), and .map_windows() (use .windows() on a slice). See Result and Slices.
Fix 3: Update Rust if the feature was stabilized recently
Section titled “Fix 3: Update Rust if the feature was stabilized recently”Choose this when the docs show a version number instead of the 🔬 box, or the same code compiles for a friend or on the Playground. Then the feature is stable, just newer than your compiler. For example, as_chunks on slices became stable in Rust 1.88. On an older Rust you’d get E0658 for it; on a current one it simply works:
fn main() { let readings = [21, 22, 19, 20, 23]; let (pairs, rest) = readings.as_chunks::<2>(); for [a, b] in pairs { println!("{} and {}", a, b); } println!("left over: {:?}", rest);}Output:
21 and 2219 and 20left over: [23]Check your version and update:
rustc --versionrustup updateIf rustc --version still shows an old version afterwards, look for a rust-toolchain.toml file in your project. It pins the project to one Rust version, and rustup show tells you which one is active. See Rust Tools & Setup.
Fix 4: Opt into nightly with #![feature], only if you really mean it
Section titled “Fix 4: Opt into nightly with #![feature], only if you really mean it”Choose this last, and only when there is no stable alternative and you’re fine with your code breaking on a future nightly. You need two things: a #![feature(...)] line at the top of main.rs (or lib.rs) with the feature name from the error, and the nightly compiler. On stable, the #![feature] line itself is rejected:
error[E0554]: `#![feature]` may not be used on the stable release channel --> src/main.rs:1:1 |1 | #![feature(iter_intersperse)] // ❌ only allowed on nightly Rust | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^The program, which only builds on nightly:
#![feature(iter_intersperse)] // ❌ only allowed on nightly Rust
fn main() { let tags = ["rust", "web", "cli"]; let joined: String = tags.iter().copied().intersperse(", ").collect(); println!("{}", joined);}Install nightly next to your stable Rust and use it just for this project or this command:
rustup toolchain install nightlycargo +nightly runBe honest with yourself here. For learning, tests and small tools, nightly is fine. For a library other people depend on, or anything you have to maintain, stay on stable: most of the crates you use do too.
Where you’ll usually see it
Section titled “Where you’ll usually see it”Vec<i32, i32> when you meant a list of pairs. Vec’s second parameter is the memory allocator, which is unstable, so the first error is about allocator_api (a few more errors follow it):
error[E0658]: use of unstable library feature `allocator_api` --> src/main.rs:2:26 |2 | let points: Vec<i32, i32> = vec![]; // ❌ meant a list of (x, y) pairs | -----^^^ | | | help: consider wrapping the inner types in tuple: `(i32, i32)`fn main() { let points: Vec<i32, i32> = vec![]; // ❌ meant a list of (x, y) pairs println!("{} points", points.len());}Fix: do what the help says, Vec<(i32, i32)>. See Vectors and Tuples. The same mistake on Option or Result is E0107.
The ! type in your own signatures. ! means “never happens”. It works as the return type of a function that never returns, but anywhere else it’s still unstable:
error[E0658]: the `!` type is experimental --> src/main.rs:1:34 |1 | fn default_port() -> Result<u16, !> { // ❌ the `!` type is nightly-only here | ^fn default_port() -> Result<u16, !> { // ❌ the `!` type is nightly-only here Ok(8080)}
fn main() { println!("{:?}", default_port());}Fix: use std::convert::Infallible, the stable “this error can’t happen” type, or just return u16 if nothing can fail.
try blocks. A try { ... } block lets you use ? without writing a function, but it’s nightly-only (`try` expression is experimental). Fix: move the code into a small function that returns Result and use ? there, as in Fix 2. See Result.
Related, but not E0658: code written for the 2024 edition, like if let Some(a) = age && a >= 18, gives let chains are only allowed in Rust 2024 or later on an older edition. Fix: set edition = "2024" in Cargo.toml, or nest the two ifs.
Related errors
Section titled “Related errors”- E0554:
#![feature]may not be used on the stable release channel, the follow-up error when you try Fix 4 on stable. - E0599: no method found; when the method doesn’t exist at all (or comes from a crate you haven’t added), you get this instead.
- E0107: wrong number of generic arguments, like
Option<i32, i32>(the cousin of theVec<i32, i32>case). - E0277: trait bound not satisfied; one of the errors that follows
Vec<i32, i32>. - E0463: can’t find crate, if you switch to
itertoolsand forget to add it toCargo.toml. - All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”Quick check
1. The std docs page for a method shows a box saying "This is a nightly-only experimental API". What happens if you call it on stable Rust?
2. .try_collect() gives E0658. What's the usual stable way to turn an iterator of Results into Result<Vec<_>, _>?
3. Code that compiles on the Playground gives you E0658 for a method whose docs show 1.88.0. What should you try first?
Score: 0 / 3
Read more
Section titled “Read more”- E0658 — Rust error codes index: the official explanation, and how a
#![feature]line enables a feature on nightly. - How Rust is Made and “Nightly Rust” — The Rust Book: stable, beta and nightly, the release train, and how unstable features become stable.
- Channels — The rustup book: installing nightly,
cargo +nightly, and setting a toolchain per project. - The Unstable Book: every unstable feature name you can see in an E0658, with a short description of each.
slice::join— standard library docs: the stable way to join strings with a separator, the alternative tointersperse.