Skip to content

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.)

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 information

This 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);
}

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.

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.

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, cli
RUST | WEB | CLI
▶ Try it Yourself

If 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 information

The 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 })
▶ Try it Yourself

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 22
19 and 20
left over: [23]
▶ Try it Yourself

Check your version and update:

Terminal window
rustc --version
rustup update

If 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:

Terminal window
rustup toolchain install nightly
cargo +nightly run

Be 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.

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.

  • 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 the Vec<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 itertools and forget to add it to Cargo.toml.
  • All compiler errors: the full list of error pages.

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