Ir al contenido

Error E0658 de Rust: use of unstable library feature (función experimental)

E0658 significa: usaste algo que existe en Rust, pero que todavía no está terminado, así que Rust estable no te deja usarlo. Normalmente es un método que encontraste en la documentación de la biblioteca estándar o en un artículo de blog, como .intersperse() o .try_collect(), o una parte de la sintaxis como try { ... }. Tu código no está mal, y en tu ordenador no hay nada roto. Simplemente echaste mano de algo que solo funciona con el compilador nightly. Casi siempre hay una forma estable de hacer lo mismo en una línea. (Esta página forma parte de Errores del compilador de Rust explicados.)

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

En español: “uso de una función inestable de la biblioteca, iter_intersperse”. La note te manda al issue de GitHub donde tienes más información.

Este es el programa que lo produce:

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

El ^^^^^^^^^^^ señala intersperse en la línea 3. El titular da el nombre de la funcionalidad (feature) entre comillas invertidas, iter_intersperse. Es el nombre que usa el equipo de Rust para esta parte sin terminar de la biblioteca estándar. La note: enlaza el issue de GitHub donde se está discutiendo.

O sea, rustc encontró el método. Sabe exactamente lo que quieres decir. Solo que no lo compila, porque intersperse sigue marcado como inestable (unstable): puede cambiar de nombre, de argumentos o de comportamiento antes de estar terminado, o incluso desaparecer.

Otras formas del titular significan lo mismo para funcionalidades del lenguaje en lugar de la biblioteca: the `!` type is experimental (el tipo ! es experimental), `try` expression is experimental (la expresión try es experimental) o associated type defaults are unstable (los valores por defecto de los tipos asociados son inestables).

Es como una tienda que tiene un producto nuevo en la estantería con una pegatina de “solo personal, todavía en pruebas”. Lo ves y puedes leer la etiqueta, pero la caja todavía no te lo vende.

Rust hace una promesa fuerte: el código que hoy compila en Rust estable seguirá compilando en todas las versiones estables futuras de Rust 1.x. Para cumplirla, las funcionalidades nuevas llegan primero al canal nightly, donde la gente puede probarlas y el diseño todavía puede cambiar. Solo cuando una funcionalidad está terminada pasa a estable, normalmente después de meses o años de pruebas.

Rust estable se niega a compilar cualquier funcionalidad inestable, para que no construyas sobre algo que podría cambiar debajo de ti. Si de verdad quieres probar una, nightly te deja activarla por su nombre con #![feature(iter_intersperse)]. En estable, incluso esa línea es un error (E0554).

La documentación de la biblioteca estándar muestra los dos tipos uno al lado del otro. Los elementos estables tienen un número de versión a la derecha, como 1.82.0: la versión de Rust en la que pasaron a estable. Los inestables tienen un recuadro que dice “🔬 This is a nightly-only experimental API.” (esta es una API experimental solo para nightly). Ese recuadro es la forma más rápida de detectar un E0658 antes de escribir una sola línea. Mira Herramientas de Rust para ver cómo encajan los canales de Rust y rustup.

Elige esta casi siempre. Los métodos inestables suelen ser atajos para algo que Rust estable ya sabe hacer. Para unir cadenas de texto con un separador, join sobre un slice o un Vec hace lo que haría intersperse(...).collect():

fn main() {
let tags = ["rust", "web", "cli"];
let joined = tags.join(", ");
println!("{}", joined);
// Cadenas creadas por un iterador: primero recógelas en un Vec y luego únelas.
let upper: Vec<String> = tags.iter().map(|t| t.to_uppercase()).collect();
println!("{}", upper.join(" | "));
}

Salida:

rust, web, cli
RUST | WEB | CLI
▶ Pruébalo tú mismo

Si prefieres dejarlo todo en una sola cadena de iteradores, el popular crate itertools tiene join e intersperse estables para iteradores. Mira Strings e Iteradores.

Solución 2: Busca en la documentación la marca “nightly-only”

Sección titulada «Solución 2: Busca en la documentación la marca “nightly-only”»

Elige esta cuando copiaste un método de la documentación, de una respuesta de una IA o de un blog. Abre el método en la documentación de std: si tiene el recuadro 🔬, busca un poco más la forma estable. Un caso frecuente es .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

El programa con el error:

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 estable ya hace esto con un simple collect(). Cuando los elementos son Result y los recoges en un Result<Vec<_>, _>, obtienes Ok con todos los valores, o el primer 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"]));
}

Salida:

Ok([3, 14, 15])
Err(ParseIntError { kind: InvalidDigit })
▶ Pruébalo tú mismo

Otros métodos inestables con los que te puedes encontrar, con su alternativa estable: .array_chunks() sobre un iterador (iter_array_chunks; usa .as_chunks() o .chunks() sobre un slice) y .map_windows() (usa .windows() sobre un slice). Mira Result y Slices.

Solución 3: Actualiza Rust si la funcionalidad se estabilizó hace poco

Sección titulada «Solución 3: Actualiza Rust si la funcionalidad se estabilizó hace poco»

Elige esta cuando la documentación muestra un número de versión en lugar del recuadro 🔬, o cuando el mismo código compila en el ordenador de un amigo o en el Playground. Entonces la funcionalidad sí es estable, solo que es más nueva que tu compilador. Por ejemplo, as_chunks sobre slices pasó a estable en Rust 1.88. Con un Rust más antiguo te daría E0658; con uno actual simplemente funciona:

fn main() {
let readings = [21, 22, 19, 20, 23];
let (pairs, rest) = readings.as_chunks::<2>();
for [a, b] in pairs {
println!("{} y {}", a, b);
}
println!("sobran: {:?}", rest);
}

Salida:

21 y 22
19 y 20
sobran: [23]
▶ Pruébalo tú mismo

Comprueba tu versión y actualiza:

Terminal window
rustc --version
rustup update

Si después rustc --version sigue mostrando una versión antigua, busca un archivo rust-toolchain.toml en tu proyecto. Fija el proyecto a una versión concreta de Rust, y rustup show te dice cuál está activa. Mira Herramientas de Rust.

Solución 4: Pasa a nightly con #![feature], solo si de verdad lo quieres

Sección titulada «Solución 4: Pasa a nightly con #![feature], solo si de verdad lo quieres»

Elige esta la última, y solo cuando no haya alternativa estable y te parezca bien que tu código se rompa con un nightly futuro. Necesitas dos cosas: una línea #![feature(...)] al principio de main.rs (o de lib.rs) con el nombre de la funcionalidad que aparece en el error, y el compilador nightly. En estable, la propia línea #![feature] se rechaza:

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

“#![feature] no se puede usar en el canal estable”. El programa, que solo compila en 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);
}

Instala nightly junto a tu Rust estable y úsalo solo para este proyecto o para este comando:

Terminal window
rustup toolchain install nightly
cargo +nightly run

Sé sincero contigo mismo aquí. Para aprender, para tests y para herramientas pequeñas, nightly está bien. Para una biblioteca de la que dependen otras personas, o para cualquier cosa que tengas que mantener, quédate en estable: la mayoría de los crates que usas también lo hacen.

Vec<i32, i32> cuando querías una lista de pares. El segundo parámetro de Vec es el asignador de memoria (allocator), que es inestable, así que el primer error habla de allocator_api (y le siguen unos cuantos errores más):

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

Solución: haz lo que dice el help (envolver los tipos en una tupla), Vec<(i32, i32)>. Mira Vectores y Tuplas. El mismo error con Option o Result es E0107.

El tipo ! en tus propias firmas. ! significa “esto nunca pasa”. Funciona como tipo de retorno de una función que nunca termina, pero en cualquier otro sitio sigue siendo inestable:

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

Solución: usa std::convert::Infallible, el tipo estable que significa “este error no puede ocurrir”, o simplemente devuelve u16 si nada puede fallar.

Bloques try. Un bloque try { ... } te deja usar ? sin escribir una función, pero solo existe en nightly (`try` expression is experimental). Solución: mueve el código a una función pequeña que devuelva Result y usa ? ahí, como en la Solución 2. Mira Result.

Relacionado, pero no es E0658: el código escrito para la edición 2024, como if let Some(a) = age && a >= 18, da let chains are only allowed in Rust 2024 or later (las cadenas de let solo se permiten en Rust 2024 o posterior) en una edición más antigua. Solución: pon edition = "2024" en Cargo.toml, o anida los dos if.

  • E0554: #![feature] may not be used on the stable release channel, el error que viene después cuando pruebas la Solución 4 en estable.
  • E0599: no se encontró el método; si el método no existe en absoluto (o viene de un crate que no has añadido), te sale este en su lugar.
  • E0107: número equivocado de argumentos genéricos, como Option<i32, i32> (el primo del caso Vec<i32, i32>).
  • E0277: no se cumple un trait bound; uno de los errores que siguen a Vec<i32, i32>.
  • E0463: can’t find crate, si te pasas a itertools y olvidas añadirlo a Cargo.toml.
  • Todos los errores del compilador: la lista completa de páginas de errores.

Comprueba lo aprendido

1. La página de un método en la documentación de std muestra un recuadro que dice "This is a nightly-only experimental API". ¿Qué pasa si lo llamas en Rust estable?

2. .try_collect() da E0658. ¿Cuál es la forma estable habitual de convertir un iterador de Result en Result<Vec<_>, _>?

3. Un código que compila en el Playground te da E0658 por un método cuya documentación muestra 1.88.0. ¿Qué deberías probar primero?

Puntuación: 0 / 3