Error E0308 de Rust: mismatched types (tipos que no coinciden)
El error E0308 significa que Rust esperaba un valor de un tipo, pero le diste un valor de otro tipo distinto. Seguramente es el error que más vas a ver en Rust, y el mensaje casi siempre te dice los dos tipos, así que normalmente se arregla rápido. (¿Buscas otro error? Mira la lista de errores del compilador).
El mensaje de error
Sección titulada «El mensaje de error»error[E0308]: mismatched types --> src/main.rs:6:29 |6 | let user = User { name: "Ferris" }; | ^^^^^^^^ expected `String`, found `&str` |help: try using a conversion method |6 | let user = User { name: "Ferris".to_string() }; | ++++++++++++El compilador dice “tipos que no coinciden”: esperaba un String y encontró un &str. Y en la parte help: te propone un método de conversión.
Este es el programa que lo provoca:
struct User { name: String,}
fn main() { let user = User { name: "Ferris" }; println!("{}", user.name);}Qué significa
Sección titulada «Qué significa»Todos los mensajes E0308 tienen la misma forma: expected A, found B (se esperaba A, se encontró B).
- Expected es el tipo que Rust necesita en ese sitio. Viene de otra parte del código: una anotación de tipo, el parámetro o el tipo de retorno de una función, el campo de una struct u otra rama de un
if/match. - Found es el tipo del valor que realmente escribiste ahí.
En el ejemplo, el campo name se declaró como String, así que eso es lo que Rust espera. Pero "Ferris" es un literal de cadena, y su tipo es &str. Están relacionados, pero no son el mismo tipo.
Fíjate también en las líneas help:. Con E0308, el compilador a menudo te sugiere la solución exacta, como el .to_string() de arriba.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»Rust nunca convierte entre tipos sin avisar. En algunos lenguajes, un número se convierte en texto sin que te des cuenta, o un número grande se recorta para caber en uno más pequeño. Esas conversiones ocultas causan bugs reales: datos perdidos, resultados incorrectos y fallos que aparecen lejos del error de verdad.
Por eso Rust hace que cada conversión sea visible en el código. Escribes un poco más, pero cuando vuelves a leer el código ves exactamente dónde cambia de tipo un valor y si ese paso puede fallar. Los tipos en sí los tienes en Tipos de datos.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»Cada solución empieza con la forma del error, para que saltes directamente a la que se parece a la tuya.
Solución 1: Quita el punto y coma del final
Sección titulada «Solución 1: Quita el punto y coma del final»error[E0308]: mismatched types --> src/main.rs:1:46 |1 | fn total_price(price: u32, quantity: u32) -> u32 { | ----------- ^^^ expected `u32`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression2 | price * quantity; | - help: remove this semicolon to return this valueAquí el compilador dice que la función devuelve () sin querer, porque su cuerpo no termina en una expresión ni en un return, y te sugiere quitar el punto y coma.
Si ves found (), revisa la última línea de la función. En Rust, la última expresión de un bloque sin punto y coma es su valor de retorno. Al añadir ; la conviertes en una instrucción, y la función devuelve () (el tipo “nada”). Borra el punto y coma:
fn total_price(price: u32, quantity: u32) -> u32 { price * quantity // sin punto y coma: se devuelve este valor}
fn main() { println!("Total: {}", total_price(250, 3));}Salida:
Total: 750Lo mismo pasa dentro de los bloques if y de las ramas de match. Más detalles en Funciones.
Solución 2: Convierte entre String y &str
Sección titulada «Solución 2: Convierte entre String y &str»String es una cadena de texto con dueño que puede crecer. &str es una vista prestada de un texto. La historia completa está en Strings. La conversión es corta en los dos sentidos:
- De
&straString:.to_string()oString::from(...). - De
Stringa&str: préstalo con&(o llama a.as_str()).
El segundo caso aparece como expected &str, found String, normalmente cuando pasas un String a una función que recibe &str.
struct User { name: String,}
fn greet(name: &str) { println!("¡Hola, {name}!");}
fn main() { // &str -> String: .to_string() o String::from() let user = User { name: "Ferris".to_string() };
// String -> &str: préstalo con & (o llama a .as_str()) greet(&user.name); greet(user.name.as_str());
let mut tags: Vec<String> = Vec::new(); tags.push(String::from("rust")); println!("{tags:?}");}Salida:
¡Hola, Ferris!¡Hola, Ferris!["rust"]Solución 3: Convierte entre tipos numéricos
Sección titulada «Solución 3: Convierte entre tipos numéricos»error[E0308]: mismatched types --> src/main.rs:3:22 |3 | let count: i32 = names.len(); | --- ^^^^^^^^^^^ expected `i32`, found `usize` | | | expected due to thisEl compilador esperaba un i32 (por la anotación, “expected due to this”) y encontró un usize.
i32, i64, u32 y usize son tipos distintos, y Rust no los mezcla. Las longitudes y los índices siempre son usize. Elige una conversión:
.into()(oi64::from(x)) cuando la conversión nunca puede perder datos, como dei32ai64.i64::try_from(x)(o.try_into()) cuando el valor podría no caber. Devuelve unResult, así que tú decides qué pasa si falla.ases la conversión rápida. Nunca falla, pero recorta sin avisar los valores que no caben, así que úsala solo cuando sepas que el valor es pequeño.
fn average(total: i64, count: i64) -> i64 { total / count}
fn main() { let scores: Vec<i32> = vec![80, 95, 70]; let total: i32 = scores.iter().sum();
// i32 -> i64 siempre cabe, así que basta con .into() let total: i64 = total.into();
// usize -> i64 podría no caber, así que try_from lo comprueba antes let count = i64::try_from(scores.len()).unwrap();
println!("Media: {}", average(total, count));
// `as` también funciona, pero recorta sin avisar los valores que no caben let players = scores.len() as i32; println!("{players} jugadores");}Salida:
Media: 813 jugadoresMuchas veces lo más sencillo es cambiar la anotación: let count: usize = names.len();. Las páginas de Conversión de tipos y Conversiones profundizan más.
Solución 4: Envuelve en Some, o saca el valor del Option
Sección titulada «Solución 4: Envuelve en Some, o saca el valor del Option»error[E0308]: mismatched types --> src/main.rs:3:27 |3 | let position: usize = names.iter().position(|n| *n == "Ben"); | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Option<usize>` | | | expected due to thisAquí Rust esperaba un usize y encontró un Option<usize>.
Option<usize> no es un usize. Es “quizá un usize, quizá nada”. Este desajuste puede ir en los dos sentidos:
- Expected
T, foundOption<T>: tienes que ocuparte del caso “nada”, conmatch,if letounwrap_or(valor_por_defecto). - Expected
Option<T>, foundT: tu función devuelveOption, así que envuelve el valor:Some(10)en lugar de10.
fn find_discount(code: &str) -> Option<u32> { if code == "SAVE10" { return Some(10); // T -> Option<T>: envuélvelo en Some } None}
fn main() { let names = vec!["Ana", "Ben", "Cy"];
// Option<T> -> T: ocúpate del caso "no se encontró nada"... match names.iter().position(|n| *n == "Ben") { Some(i) => println!("Ben está en la posición {i}"), None => println!("Ben no está aquí"), }
// ...o elige un valor por defecto con unwrap_or let discount: u32 = find_discount("HELLO").unwrap_or(0); println!("Descuento: {discount}%");}Salida:
Ben está en la posición 1Descuento: 0%Puede que el compilador te sugiera .expect("REASON"). También funciona, pero el programa se detiene con un panic cuando el valor es None, así que úsalo solo cuando None de verdad no pueda ocurrir. Mira Option.
Solución 5: Haz que todas las ramas de if/else y match tengan el mismo tipo
Sección titulada «Solución 5: Haz que todas las ramas de if/else y match tengan el mismo tipo»error[E0308]: `if` and `else` have incompatible types --> src/main.rs:3:52 |3 | let label = if stock > 0 { "in stock" } else { stock }; | ---------- ^^^^^ expected `&str`, found integer | | | expected because of thisEl mensaje dice que if y else tienen tipos incompatibles: la primera rama da un &str y la segunda un entero.
Cuando un if o un match produce un valor, Rust necesita saber su único tipo. Así que todas las ramas deben producir el mismo tipo. La primera rama marca lo que se espera, y el error señala la rama que no encaja. Con match, el mensaje dice “match arms have incompatible types”. Haz que la rama rara coincida con las demás:
fn status_text(code: u16) -> String { match code { 200 => "OK".to_string(), 404 => "No encontrado".to_string(), _ => format!("Código desconocido {code}"), // todas las ramas son String }}
fn main() { let stock = 0; let label = if stock > 0 { "disponible" } else { "agotado" }; // las dos son &str println!("{label}");
println!("{}", status_text(404)); println!("{}", status_text(418));}Salida:
agotadoNo encontradoCódigo desconocido 418Aquí una rama necesitaba format!, que crea un String, así que las otras ramas también pasaron a ser String. Mira If…Else y Match.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»- Al leer lo que escribe el usuario.
let age: u32 = input.trim();da “expectedu32, found&str”. Un texto no es un número. Solución: conviértelo conparse:let age: u32 = input.trim().parse().unwrap();(o maneja el error, mira Result). - Al meter literales en un
Vec<String>.tags.push("rust")da “expectedString, found&str”. Solución:tags.push("rust".to_string()), o usa unVec<&str>si todas las cadenas son literales. - Al hacer cuentas con números de distinto tipo.
total + n, contotalde tipoi64ynde tipoi32, da E0308 (y a menudo E0277 al lado). Solución: convierte primero uno de los lados:total + i64::from(n).
Errores relacionados
Sección titulada «Errores relacionados»- E0277: trait bound not satisfied (no se cumple el trait): suele aparecer junto a E0308, por ejemplo cuando sumas un
i64a uni32. - E0599: no method found (método no encontrado): llamaste a un método que no existe para ese tipo, a menudo porque el valor tiene otro tipo del que pensabas.
- E0425: cannot find value in this scope (valor no encontrado): un nombre que Rust no conoce en absoluto.
- 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. Una función dice -> i32, pero el error dice "expected i32, found ()". ¿Cuál es la causa más probable?
2. Una función recibe &str y tú tienes let name = String::from("Ana");. ¿Cómo se lo pasas?
3. ¿Qué conversión nunca puede perder datos sin avisar?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0308 — índice de códigos de error de Rust (en inglés): la explicación oficial de este error.
- Funciones con valores de retorno — El libro de Rust (en inglés): por qué un punto y coma al final cambia lo que devuelve una función.
- Guardar texto UTF-8 con Strings — El libro de Rust (en inglés): cómo se relacionan
Stringy&stry cómo convertir entre ellos. - Conversion — Rust by Example (en inglés):
From,Into,TryFromyTryIntocon ejemplos cortos. - Option — biblioteca estándar de Rust (en inglés): todos los métodos para sacar un valor de un
Option, comounwrap_orymap.