Error E0282 de Rust: type annotations needed (faltan anotaciones de tipo)
E0282 significa que Rust intentó averiguar un tipo por su cuenta y no encontró pistas suficientes. Tu lógica no tiene nada de malo. El compilador solo necesita que le digas un tipo, normalmente con unos pocos caracteres más. Esta página te enseña primero la solución y después explica cómo Rust deduce los tipos (lo que se llama inferencia de tipos), para que sepas cuándo echarle una mano. 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[E0282]: type annotations needed for `Vec<_>` --> src/main.rs:2:9 |2 | let mut scores = Vec::new(); // un Vec de... ¿qué? | ^^^^^^^^^^ ---------- type must be known at this point |help: consider giving `scores` an explicit type, where the type for type parameter `T` is specified |2 | let mut scores: Vec<T> = Vec::new(); // un Vec de... ¿qué? | ++++++++El compilador dice que necesita una anotación de tipo para el Vec<_>: tiene que conocer el tipo en ese punto, y te sugiere darle a scores un tipo explícito.
Este es el programa que lo provoca:
fn main() { let mut scores = Vec::new(); // un Vec de... ¿qué? println!("Scores recorded: {}", scores.len());}El mismo mensaje, otro código
Sección titulada «El mismo mensaje, otro código»Las palabras “type annotations needed” también aparecen con otros tres códigos. Significan lo mismo, y las soluciones de esta página sirven para todos:
- E0283:
.collect()o.sum()sin tipo de destino. rustc añade una nota comothe type must implement `FromIterator<&str>`o`Sum<&i32>`, porque el resultado podría ser de muchos tipos. - E0284:
"42".parse().unwrap()sin tipo de destino. La nota dicecannot satisfy `<_ as FromStr>::Err == _`, que es una forma aterradora de decir “¿convertir en qué?”. - E0790:
let settings = Default::default();. El titular dicecannot call associated function on trait without specifying the corresponding `impl` type: Rust no sabe el valor por defecto de quién quieres.
El código que te sale depende de la línea exacta, y puede cambiar si añades más código. Por ejemplo, let age = "42".parse().unwrap(); da E0284, pero si en la línea siguiente llamas a age.pow(2), pasa a ser E0282. No te preocupes por el número; busca la variable que señala rustc.
Qué significa
Sección titulada «Qué significa»Mira las marcas bajo la línea 2:
^^^^^^^^^^bajomut scores: esta es la variable cuyo tipo Rust no pudo averiguar.----------bajoVec::new(): “type must be known at this point” (el tipo tiene que conocerse en este punto).Vec::new()crea una lista vacía, pero una lista vacía ¿de qué? ¿Números? ¿Nombres? Rust necesita saberlo para reservar el tipo de espacio correcto.- La ayuda sugiere
let mut scores: Vec<T>. AquíTsolo ocupa el lugar. Tú lo cambias por un tipo real, comoVec<u32>.
Piensa en un paquete en la oficina de correos con el campo “contenido” en blanco. El empleado no puede enviarlo hasta que alguien escriba algo ahí. Puedes escribirlo tú, o meter algo en la caja para que el empleado lo vea.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»Cada valor en Rust tiene un único tipo exacto, decidido al compilar. Casi siempre no lo escribes, porque Rust lee tu código y lo deduce. let count = 5; es un i32. let name = String::from("Ferris"); es un String. Eso es la inferencia de tipos, y la lección de Variables muestra más ejemplos.
La inferencia usa pistas de toda la función, no solo de una línea. Si más adelante llamas a scores.push(90), eso es una pista: el Vec guarda números. Si pasas scores a una función que recibe &[u32], la pista es todavía mejor. E0282 aparece cuando no hay ninguna pista, o cuando Rust necesita el tipo antes de llegar a la pista (por ejemplo, para buscar un método).
Algunas funciones son genéricas en su tipo de retorno: parse, collect, sum, Default::default, Vec::new, None. Pueden producir muchos tipos distintos, así que Rust no puede elegir uno por ti. Y no va a adivinar, porque un error al adivinar cambiaría sin avisar el comportamiento de tu programa. Mira Genéricos para ver cómo una función puede devolver muchos tipos.
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. Añade : Tipo después del nombre de la variable. Es corto, y además documenta tu código para quien lo lea después:
fn main() { let scores: Vec<u32> = Vec::new(); let age: u32 = "42".parse().unwrap(); let words: Vec<&str> = "rojo verde azul".split(' ').collect(); let winner: Option<String> = None;
println!("Puntuaciones registradas: {}", scores.len()); println!("El año que viene tendrás {}", age + 1); println!("{:?}", words); println!("{:?}", winner);}Salida:
Puntuaciones registradas: 0El año que viene tendrás 43["rojo", "verde", "azul"]NoneNo siempre tienes que escribir el tipo completo. let words: Vec<_> = ...collect(); significa “un Vec, y lo que va dentro lo averiguas tú”. El _ le pide a Rust que rellene esa parte. Más sobre cómo escribir tipos en Tipos de datos.
Solución 2: Usa el turbofish ::<>
Sección titulada «Solución 2: Usa el turbofish ::<>»Elige esta cuando no hay ninguna variable que anotar, por ejemplo en medio de una cadena de llamadas de iteradores. Pones el tipo directamente en la llamada a la función con ::<Tipo>. A ::<> lo llaman “turbofish” porque se parece un poco a un pez:
fn main() { let input = "4 15 7"; let numbers = input .split(' ') .map(|s| s.parse::<u32>().unwrap()) .collect::<Vec<_>>(); let total = numbers.iter().sum::<u32>();
println!("{:?}", numbers); println!("Total: {total}");}Salida:
[4, 15, 7]Total: 26Las tres llamadas con turbofish más comunes son parse::<i32>(), collect::<Vec<_>>() y sum::<i32>(). Las soluciones 1 y 2 son igual de correctas; let total: u32 = numbers.iter().sum(); hace lo mismo. Elige la que se lea mejor.
Solución 3: Usa el valor, para que Rust deduzca el tipo
Sección titulada «Solución 3: Usa el valor, para que Rust deduzca el tipo»Elige esta cuando el error aparece porque tu código todavía no está terminado. Muchas veces creas un Vec vacío y el código que lo llena viene después. En cuanto usas el valor, Rust tiene su pista y el error desaparece, sin necesidad de anotación:
fn print_team(names: &[String]) { for name in names { println!("- {name}"); }}
fn main() { let mut team = Vec::new(); // tipo desconocido... por ahora team.push(String::from("Ferris")); // ahora Rust lo sabe: Vec<String> team.push(String::from("Corro")); print_team(&team);}Salida:
- Ferris- CorroEl orden importa un poco. La pista tiene que llegar antes de que Rust necesite el tipo para llamar a un método. Una closure como let shout = |s| s.to_uppercase(); sigue fallando aunque llames a shout("hola") en la línea siguiente, porque Rust tiene que saber qué es s para encontrar .to_uppercase(). Ahí, escribe el tipo: |s: &str| s.to_uppercase(). Mira Closures.
Solución 4: Indica el tipo de None y de Default::default()
Sección titulada «Solución 4: Indica el tipo de None y de Default::default()»Elige esta para valores que no dan ninguna pista. None no dice qué contendría, y Default::default() no dice el valor por defecto de quién quieres. Escribe el tipo dentro de la expresión:
#[derive(Debug, Default)]struct Settings { volume: u8, muted: bool,}
fn main() { let fastest_lap = None::<f64>; // en lugar de: None let settings = Settings::default(); // en lugar de: Default::default() let backup: Settings = Default::default(); // una anotación también sirve
println!("{:?}", fastest_lap); println!("{:?}", settings); println!("Volumen de la copia: {}", backup.volume);}Salida:
NoneSettings { volume: 0, muted: false }Volumen de la copia: 0Settings::default() es la opción más clara: se lee como “los Settings por defecto”. None::<f64> es práctico, pero let fastest_lap: Option<f64> = None; (solución 1) es más habitual. Mira Option.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»Convertir texto en un número. let guess = line.trim().parse().unwrap(); da E0284 (o E0282 si después llamas a un método de guess). parse puede producir un i32, un u8, un f64 y muchos más. Solución: let guess: i32 = ... o .parse::<i32>(). Comparar guess más adelante con una variable i32 no basta aquí, porque parse necesita su tipo en la propia llamada.
.collect(). Puede construir un Vec, un String, un HashSet, un HashMap… así que siempre necesita saber el destino. Solución: let words: Vec<_> = ...collect(); o .collect::<Vec<_>>(). Mira Iteradores.
.sum() y .product(). nums.iter().sum() da E0283 con the type must implement `Sum<&i32>`. Sumar i32 podría dar un i32, pero otros tipos numéricos también pueden ser sumas. Solución: let total: i32 = nums.iter().sum(); o .sum::<i32>().
Una colección vacía en un constructor. let cache = HashMap::new(); en una función new() que todavía no has terminado de escribir. Solución: escribe el tipo completo una vez (HashMap<String, u32>), o devuélvela dentro de una struct cuyo campo ya tenga el tipo declarado, que es una pista que Rust puede usar.
Errores relacionados
Sección titulada «Errores relacionados»- E0308: mismatched types (tipos que no coinciden). Pasa cuando tu anotación y el valor no están de acuerdo, como
let age: u32 = "42";. - E0277: trait bound not satisfied (trait no implementado). Diste un tipo, pero no se puede construir de esa forma, como
let n: Vec<i32> = "1 2".split(' ').collect();(eso es unVec<&str>; convierte antes cada elemento). - E0599: no method found (método no encontrado), por ejemplo cuando anotas
let n: u32y luego llamas a un método deStringcomo.trim(). - E0004: non-exhaustive patterns (match no exhaustivo), otro error en el que rustc te escribe la solució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é línea arregla el E0282 de let ids = Vec::new(); cuando el Vec debe guardar valores u64?
2. ¿Por qué let n = "7".parse().unwrap(); necesita un tipo, si el texto es claramente un número?
3. ¿Qué significa el _ en .collect::<Vec<_>>()?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0282 — índice de códigos de error de Rust (en inglés): la explicación oficial de este error, con más ejemplos.
- E0283 — índice de códigos de error de Rust (en inglés): la variante que verás con
collectysum, cuando encajarían muchos tipos. - Tipos de datos — El libro de Rust (en inglés): explica las anotaciones, incluido el clásico ejemplo de
"42".parse(). - Inference — Rust by Example (en inglés): un ejemplo corto de Rust deduciendo el tipo de un
Veca partir del código que viene después. Iterator::collect— documentación de la biblioteca estándar (en inglés): muestra tanto el estilo con anotación como el estilo turbofish.