Перейти к содержимому

Ошибка Rust E0658: use of unstable library feature (нестабильная возможность)

E0658 значит: ты использовал то, что в Rust уже есть, но ещё не доделано, поэтому стабильный Rust не даёт этим пользоваться. Обычно это метод, который ты нашёл в документации стандартной библиотеки или в статье, например .intersperse() или .try_collect(), либо конструкция языка вроде try { ... }. Твой код не ошибочный, и на твоём компьютере ничего не сломано. Ты просто потянулся к возможности, которая работает только в компиляторе nightly. Почти всегда есть стабильный способ сделать то же самое одной строкой. (Эта страница входит в раздел Ошибки компилятора Rust с объяснениями.)

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

По-русски: «использование нестабильной возможности библиотеки iter_intersperse». В заметке rustc даёт ссылку на обсуждение этой возможности на GitHub.

Вот программа, которая её вызывает (комментарий // ❌ оставлен на английском, потому что rustc цитирует эту строку; он значит «метод только для nightly»):

fn main() {
let tags = ["rust", "web", "cli"];
let joined: String = tags.iter().copied().intersperse(", ").collect(); // ❌ nightly-only method
println!("{}", joined);
}

^^^^^^^^^^^ указывает на intersperse в строке 3. Заголовок даёт имя возможности в обратных кавычках: iter_intersperse. Так команда Rust называет эту недоделанную часть стандартной библиотеки. note: ведёт на задачу (issue) на GitHub, где её обсуждают.

Значит, rustc метод нашёл. Он точно знает, что ты имеешь в виду. Он просто не станет его компилировать, потому что intersperse всё ещё помечен как нестабильный (unstable): до завершения у него могут поменяться имя, аргументы или поведение, а может, его и вовсе уберут.

Другие формы заголовка значат то же самое, только для возможностей языка, а не библиотеки: the `!` type is experimental, `try` expression is experimental или associated type defaults are unstable.

Это как новый товар на полке магазина с наклейкой «только для сотрудников, ещё тестируем». Ты его видишь, можешь прочитать этикетку, но касса пока не даст его купить.

Rust даёт сильное обещание: код, который компилируется на стабильном Rust сегодня, будет компилироваться и на каждой будущей стабильной версии Rust 1.x. Чтобы сдержать это обещание, новые возможности сначала появляются в канале nightly, где их можно попробовать и где их устройство ещё может измениться. Стабильной возможность становится, только когда готова, — обычно после месяцев или лет испытаний.

Стабильный Rust вообще отказывается компилировать нестабильные возможности, чтобы ты не строил на том, что может поменяться у тебя под ногами. Если очень хочется попробовать, в nightly возможность можно включить по имени через #![feature(iter_intersperse)]. На stable даже эта строка — ошибка (E0554).

Документация стандартной библиотеки показывает оба вида рядом. У стабильных элементов справа стоит номер версии, например 1.82.0, — версия Rust, в которой они стали стабильными. У нестабильных есть плашка «🔬 This is a nightly-only experimental API.» («это экспериментальный API только для nightly»). Эта плашка — самый быстрый способ заметить E0658 ещё до того, как ты напишешь код. Как каналы Rust и rustup связаны между собой, рассказано в уроке Инструменты Rust.

Исправление 1: Используй стабильную альтернативу

Заголовок раздела «Исправление 1: Используй стабильную альтернативу»

Это нужно почти всегда. Нестабильные методы обычно — сокращения для того, что стабильный Rust уже умеет. Чтобы склеить строки через разделитель, join у среза или Vec делает то же, что делал бы intersperse(...).collect():

fn main() {
let tags = ["rust", "web", "cli"];
let joined = tags.join(", ");
println!("{}", joined);
// Строки, созданные итератором: сначала собери их в Vec, потом соедини.
let upper: Vec<String> = tags.iter().map(|t| t.to_uppercase()).collect();
println!("{}", upper.join(" | "));
}

Вывод:

rust, web, cli
RUST | WEB | CLI
▶ Попробуй сам

Если хочется оставить всё одной цепочкой итераторов, в популярном крейте itertools есть стабильные join и intersperse для итераторов. Подробнее — в уроках Строки и Итераторы.

Исправление 2: Проверь в документации плашку «nightly-only»

Заголовок раздела «Исправление 2: Проверь в документации плашку «nightly-only»»

Выбирай этот вариант, когда ты скопировал метод из документации, ответа ИИ или статьи. Открой метод в документации std: если там есть плашка 🔬, поищи чуть дальше стабильный способ. Частый пример — .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

Программа с ошибкой (комментарий значит «только для nightly»):

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

Стабильный Rust уже умеет это обычным collect(). Когда элементы — это Result, а собираешь ты в Result<Vec<_>, _>, получаешь Ok со всеми значениями или первую 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"]));
}

Вывод:

Ok([3, 14, 15])
Err(ParseIntError { kind: InvalidDigit })
▶ Попробуй сам

Другие нестабильные методы, которые могут попасться, и их стабильные замены: .array_chunks() у итератора (iter_array_chunks; вместо него используй .as_chunks() или .chunks() у среза) и .map_windows() (используй .windows() у среза). Подробнее — в уроках Result и Срезы.

Исправление 3: Обнови Rust, если возможность стабилизировали недавно

Заголовок раздела «Исправление 3: Обнови Rust, если возможность стабилизировали недавно»

Выбирай этот вариант, когда в документации вместо плашки 🔬 стоит номер версии или тот же код компилируется у друга или в Playground. Значит, возможность стабильна, просто она новее твоего компилятора. Например, as_chunks у срезов стал стабильным в Rust 1.88. На более старом Rust ты получил бы для него E0658, а на текущем он просто работает:

fn main() {
let readings = [21, 22, 19, 20, 23];
let (pairs, rest) = readings.as_chunks::<2>();
for [a, b] in pairs {
println!("{} и {}", a, b);
}
println!("осталось: {:?}", rest);
}

Вывод:

21 и 22
19 и 20
осталось: [23]
▶ Попробуй сам

Проверь свою версию и обнови Rust:

Terminal window
rustc --version
rustup update

Если после этого rustc --version всё ещё показывает старую версию, поищи в проекте файл rust-toolchain.toml. Он закрепляет за проектом одну версию Rust, а rustup show покажет, какая сейчас активна. Подробнее — в уроке Инструменты Rust.

Исправление 4: Включай nightly через #![feature], только если правда этого хочешь

Заголовок раздела «Исправление 4: Включай nightly через #![feature], только если правда этого хочешь»

Выбирай этот вариант в последнюю очередь — только когда стабильной альтернативы нет и тебя устраивает, что код может сломаться на будущем nightly. Нужны две вещи: строка #![feature(...)] в начале main.rs (или lib.rs) с именем возможности из ошибки и компилятор nightly. На stable отклоняется уже сама строка #![feature]:

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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

rustc пишет, что #![feature] нельзя использовать в стабильном канале. Программа, которая собирается только на nightly (комментарий значит «разрешено только в nightly Rust»):

#![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);
}

Установи nightly рядом со стабильным Rust и используй его только для этого проекта или этой команды:

Terminal window
rustup toolchain install nightly
cargo +nightly run

Будь честен с собой. Для учёбы, тестов и маленьких утилит nightly подходит. Для библиотеки, от которой зависят другие люди, или для всего, что тебе придётся поддерживать, оставайся на stable: большинство крейтов, которыми ты пользуешься, делают так же.

Vec<i32, i32>, когда ты имел в виду список пар. Второй параметр Vec — аллокатор памяти, а он нестабилен, поэтому первая ошибка будет про allocator_api (за ней последуют ещё несколько):

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

Комментарий значит «имелся в виду список пар (x, y)». Исправление: сделай, как говорит help, — Vec<(i32, i32)>. Подробнее — в уроках Векторы и Кортежи. Та же ошибка с Option или Result — это E0107.

Тип ! в твоих сигнатурах. ! значит «этого никогда не случится». Он работает как тип возврата функции, которая никогда не возвращается, но в любом другом месте пока нестабилен:

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

Комментарий значит «здесь тип ! доступен только в nightly». Исправление: используй std::convert::Infallible — стабильный тип «эта ошибка невозможна» — или просто возвращай u16, если ничего не может пойти не так.

Блоки try. Блок try { ... } позволяет использовать ? без отдельной функции, но он только для nightly (`try` expression is experimental). Исправление: перенеси код в небольшую функцию, которая возвращает Result, и используй ? там, как в исправлении 2. Подробнее — в уроке Result.

Похоже, но не E0658: код, написанный для редакции 2024, например if let Some(a) = age && a >= 18, на более старой редакции даёт let chains are only allowed in Rust 2024 or later. Исправление: укажи edition = "2024" в Cargo.toml или вложи один if в другой.

  • E0554: #![feature] may not be used on the stable release channel — ошибка-следствие, если попробовать исправление 4 на stable.
  • E0599: метод не найден; её ты получишь, если метода нет вообще (или он из крейта, который ты не добавил).
  • E0107: неверное число обобщённых аргументов, например Option<i32, i32> (родственник случая с Vec<i32, i32>).
  • E0277: ограничение трейта не выполнено; одна из ошибок, которые идут после Vec<i32, i32>.
  • E0463: крейт не найден — если перейдёшь на itertools и забудешь добавить его в Cargo.toml.
  • Все ошибки компилятора: полный список страниц об ошибках.

Проверь себя

1. На странице метода в документации std есть плашка «This is a nightly-only experimental API». Что будет, если вызвать его на стабильном Rust?

2. .try_collect() даёт E0658. Каким обычным стабильным способом превратить итератор из Result в Result<Vec<_>, _>?

3. Код компилируется в Playground, а у тебя даёт E0658 для метода, у которого в документации стоит 1.88.0. Что попробовать сначала?

Результат: 0 / 3