Error E0284 de Rust: type annotations needed (falta el tipo en parse())
E0284 casi siempre significa una sola cosa: llamaste a .parse() y Rust no sabe en qué tipo convertir el texto. La nota cannot satisfy `<_ as FromStr>::Err == _` da miedo a primera vista, pero la solución suele ser de unos pocos caracteres, como : u32 o ::<u32>. Esta página te enseña primero la solución y después descifra esa nota, para que deje de asustarte. Para ver los demás errores de esta serie, mira Errores del compilador de Rust.
El mensaje de error
Sección titulada «El mensaje de error»error[E0284]: type annotations needed --> src/main.rs:2:9 |2 | let age = "42".parse().unwrap(); | ^^^ ----- type must be known at this point | = note: cannot satisfy `<_ as FromStr>::Err == _`help: consider giving `age` an explicit type |2 | let age: /* Type */ = "42".parse().unwrap(); | ++++++++++++“Type annotations needed” quiere decir “faltan anotaciones de tipo”. Más abajo descifraremos la note pieza por pieza.
Este es el programa que lo produce:
fn main() { let age = "42".parse().unwrap(); println!("Next year you'll be {}", age + 1);}Verás la misma nota con .expect(...) en lugar de .unwrap(), con match "42".parse() { ... } y con .parse() dentro de una closure. Cuando el resultado de .parse() no se guarda en una variable, rustc señala el propio parse y sugiere parse::</* Type */>().
Qué significa
Sección titulada «Qué significa»Las marcas son la parte fácil:
^^^debajo deage: la variable cuyo tipo no se conoce.-----debajo deparse: “type must be known at this point” (el tipo tiene que conocerse en este punto). Aquí es donde Rust se quedó atascado.- El help: “consider giving
agean explicit type” (plantéate darle aageun tipo explícito). Cambia/* Type */por un tipo real, comou32.
Ahora la nota, pieza por pieza. En los mensajes del compilador, _ significa “un tipo que todavía no conozco”:
| Pieza | En palabras sencillas |
|---|---|
<_ as FromStr> |
“el tipo en el que quieres convertir el texto (todavía no lo conozco)” |
::Err |
“…y el tipo de error que viene con él” |
== _ |
“tiene que ser igual a otro tipo que tampoco conozco” |
Así que la nota entera dice: “Ni siquiera puedo averiguar qué tipo de error podría devolver parse, porque no sé en qué quieres convertir el texto.”
Piensa en darle una carta a un traductor y decirle “tradúcela”. ¿Al francés? ¿Al japonés? Hasta que no se lo digas, no puede empezar, y ni siquiera puede decirte qué problemas podría encontrarse.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»Esta es la firma de parse, un poco simplificada:
fn parse<F: FromStr>(&self) -> Result<F, F::Err>F es el tipo que quieres obtener, y F::Err es su tipo de error. Cada tipo de destino trae el suyo: convertir a i32 puede fallar con un ParseIntError, a f64 con un ParseFloatError, y a bool con un ParseBoolError. Rust necesita conocer F para saber siquiera qué devuelve parse.
Esa elección también cambia lo que pasa al ejecutar el programa. "42" se convierte sin problema en un u8, un i32 o un f64. Pero "300" falla como u8 (es demasiado grande), y "4.5" falla como i32 (no es un número entero). Rust no va a adivinar, porque una suposición equivocada se convertiría en un bug que solo aparece con ciertas entradas. La lección de Conversiones muestra cómo funciona FromStr y cómo añadirlo a tus propios tipos.
Cómo deduce Rust los tipos por su cuenta se explica en la página de E0282. Por qué hay tres códigos distintos (E0282, E0283, E0284) para “type annotations needed” se explica en la página de E0283. La versión corta: E0284 es la variante en la que la regla atascada trata de un tipo que pertenece al tipo desconocido (aquí F::Err). Para ti, la solución es la misma: di cuál es el tipo.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»Solución 1: Anota la variable
Sección titulada «Solución 1: Anota la variable»Elige esta en la mayoría de los casos. Es el estilo que usa el libro de Rust, y además deja claro qué debe contener el texto:
fn main() { let age: u32 = "42".parse().unwrap(); let price: f64 = "4.99".parse().unwrap(); let port: u16 = "8080".parse().expect("el puerto debe ser un número");
println!("El año que viene tendrás {}", age + 1); println!("Dos cafés: {:.2}", price * 2.0); println!("Escuchando en {port}");}Salida:
El año que viene tendrás 43Dos cafés: 9.98Escuchando en 8080Elige el tipo que encaje con lo que significa el texto: u16 para un puerto, u8 para un porcentaje, f64 para un precio. Un tipo demasiado pequeño convierte una entrada válida en un error, así que, si dudas, elige un tipo más holgado como i64 o u32.
Solución 2: Usa el turbofish parse::<T>()
Sección titulada «Solución 2: Usa el turbofish parse::<T>()»Elige esta cuando no hay ninguna variable que anotar: dentro de una closure, en medio de una cadena de iteradores o en una expresión como ... * 2:
fn main() { let line = "3 7 5"; let total = line .split(' ') .map(|s| s.parse::<u32>().unwrap()) .sum::<u32>(); let doubled = "21".parse::<i32>().unwrap() * 2;
println!("Total: {total}"); println!("El doble: {doubled}");}Salida:
Total: 15El doble: 42Pon el turbofish en el propio parse. Dentro de una cadena, anotar solo el resultado final muchas veces no basta; mira el primer punto de “Dónde lo verás normalmente”, más abajo.
Solución 3: Di el tipo y maneja bien el error
Sección titulada «Solución 3: Di el tipo y maneja bien el error»Elige esta para entradas reales (archivos, argumentos de la línea de comandos, formularios web), donde .unwrap() haría fallar tu programa con un texto incorrecto. Pon el tipo en parse y haz match sobre el Result. Fíjate en cómo el tipo decide qué entradas funcionan:
fn main() { for input in ["42", "300", "abc"] { match input.parse::<u8>() { Ok(n) => println!("{input}: obtuve {n}"), Err(e) => println!("{input}: {e}"), } }
let rate: Result<f64, _> = "0.25".parse(); println!("{:?}", rate);}Salida:
42: obtuve 42300: number too large to fit in target typeabc: invalid digit found in stringOk(0.25)Los mensajes de error salen en inglés porque los escribe la biblioteca estándar: “number too large to fit in target type” significa “el número es demasiado grande para el tipo de destino”, e “invalid digit found in string” significa “se encontró un dígito no válido en el texto”. En Result<f64, _> solo dices el tipo del valor. El _ deja que Rust rellene el tipo de error (ParseFloatError), que es justo la parte por la que preguntaba la nota. Más sobre esto en Result.
Solución 4: Usa el valor donde su tipo está fijado
Sección titulada «Solución 4: Usa el valor donde su tipo está fijado»Elige esta cuando el valor convertido va a un campo de un struct o a un parámetro de una función con un tipo conocido. Rust lee la función entera, así que ese uso posterior responde a la pregunta:
struct Config { port: u16, workers: u8,}
fn greet(age: u32) { println!("¡Bienvenido! Tienes {age} años.");}
fn main() { let port = "8080".parse().unwrap(); // u16, por Config let workers = "4".parse().unwrap(); // u8, por Config let config = Config { port, workers }; println!("Puerto {} con {} workers", config.port, config.workers);
let age = "42".parse().unwrap(); // u32, por greet greet(age);}Salida:
Puerto 8080 con 4 workers¡Bienvenido! Tienes 42 años.No todos los usos cuentan, y por eso esta es la Solución 4. Las operaciones como age + 1 y las comparaciones como age >= limit siguen dando E0284, aunque limit sea un u32: los operadores funcionan entre muchas parejas de tipos, así que no fijan un tipo exacto. Y llamar a un método, como age.pow(2), convierte el error en E0282. Si dudas, usa la Solución 1.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»Convertir dentro de map y después sum(). let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); sigue siendo E0284, aunque total tenga un tipo. Un u32 puede ser la suma de valores u32 o de valores &u32, así que la anotación no le dice a Rust qué es cada elemento. Solución: s.parse::<u32>(). (En cambio, recogerlos en un Vec<i32> sí funciona, porque un Vec<i32> solo puede contener i32.)
Recoger los resultados de parse en un Result. let nums: Result<Vec<_>, _> = text.split(' ').map(|x| x.parse()).collect(); es E0284: el _ de Vec<_> deja abierto el tipo de los elementos. Solución: Result<Vec<i32>, _>. Este patrón tan práctico se detiene en el primer elemento incorrecto y te da su error.
Leer lo que escribe el usuario. let guess = line.trim().parse().unwrap(); en un juego de adivinanzas o en una herramienta de línea de comandos. Solución: let guess: u32 = ..., exactamente como en el libro de Rust.
Con ? obtienes un error más raro. let port = text.parse()?; no da E0284. En la edición 2021, Rust usa por defecto el tipo () e informa de E0277: the trait bound `(): FromStr` is not satisfied (no se cumple el trait bound (): FromStr). La edición 2024 dice ! en lugar de (). La solución es la misma: let port: u16 = text.parse()?;.
Errores relacionados
Sección titulada «Errores relacionados»- E0282: faltan anotaciones de tipo cuando no hay ninguna pista (o cuando el tipo hace falta para llamar a un método, como
age.pow(2)). - E0283: faltan anotaciones de tipo después de
.sum(),.collect()o.into(), cuando encajan muchos tipos. También explica por qué son tres códigos distintos. - E0790:
FromStr::from_str("42")oDefault::default()llamados sin decir de qué tipo. - E0277: lo que te da
.parse()?sin un tipo, y lo que obtienes cuando el tipo que elegiste no se puede construir a partir de texto. - E0599: no se encontró el método, por ejemplo al llamar a
.parse()sobre algo que no es texto, como una variableu32(en ese caso conviértela conu8::try_from(n)). - Todos los errores del compilador: la lista completa de páginas de errores.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»Comprueba lo aprendido
1. ¿Qué significa cannot satisfy <_ as FromStr>::Err == _ en palabras sencillas?
2. ¿Qué línea arregla let port = "8080".parse().unwrap();?
3. let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); sigue dando E0284. ¿Dónde debe ir el tipo?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0284 — Rust error codes index (en inglés): la explicación oficial de este error.
str::parse— documentación de la biblioteca estándar (en inglés): la firma del método, con los dos estilos, anotación y turbofish.FromStr— documentación de la biblioteca estándar (en inglés): el trait que hay detrás deparse, con su tipoErry cómo implementarlo.- Programming a Guessing Game — El libro de Rust (en inglés): donde aparece por primera vez
let guess: u32 = guess.trim().parse(), explicado paso a paso. También existe una traducción al español de la comunidad del libro. - Data Types — El libro de Rust (en inglés): por qué
"42".parse()necesita una anotación de tipo, con el error real del compilador.