Error E0384 de Rust: cannot assign twice to immutable variable (sin mut)
E0384 significa que le diste un valor a una variable y luego intentaste darle otro valor, pero la variable no se declaró con mut. En Rust, las variables no se pueden cambiar a menos que lo digas tú. Casi todo el mundo se encuentra con este error en su primera hora con Rust, y la solución suele ser de tres letras: mut. En esta página verás primero la solución y después por qué Rust funciona así. Para ver los demás errores de esta serie, visita Errores del compilador de Rust.
El mensaje de error
Sección titulada «El mensaje de error»error[E0384]: cannot assign twice to immutable variable `score` --> src/main.rs:4:5 |2 | let score = 10; | ----- first assignment to `score`3 | println!("Score: {score}");4 | score = 15; // ❌ score no es mut | ^^^^^^^^^^ cannot assign twice to immutable variable |help: consider making this binding mutable |2 | let mut score = 10; | +++El compilador te dice que no puedes asignar dos veces a una variable inmutable (cannot assign twice to immutable variable) y te sugiere hacerla mutable. Este es el programa que lo provoca:
fn main() { let score = 10; println!("Score: {score}"); score = 15; // ❌ score no es mut println!("New score: {score}");}Si la variable es un parámetro de una función, el texto cambia un poco: “cannot assign to immutable argument” (no se puede asignar a un argumento inmutable). Es el mismo código de error y la misma idea.
Qué significa
Sección titulada «Qué significa»El compilador señala dos lugares:
- La línea 2, marcada con
first assignment to score(primera asignación ascore): aquí es dondescorerecibió su valor. Como la línea dicelety nolet mut, ese valor es definitivo. - La línea 4, marcada con
^^^^: aquí es donde intentaste poner un valor nuevo. Rust se niega.
La parte help: ya te muestra la solución: let mut score = 10;. Los +++ marcan los tres caracteres que hay que añadir.
Piensa en let como escribir con bolígrafo y en let mut como escribir con lápiz. Las dos cosas están bien. Pero si escribiste con bolígrafo, luego no puedes borrarlo.
La misma regla vale para +=, -=, *= y compañía. count += 1 también es una asignación, así que también necesita mut.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»En Rust, toda variable es inmutable por defecto. Para poder cambiarla tienes que pedirlo con mut. Esto se explica en la página de Variables.
Suena estricto, pero te ayuda muchísimo. Cuando lees let total = ..., sabes que total conserva ese valor hasta el final de su ámbito (scope). No tienes que revisar el resto de la función para comprobar si algo lo cambió. Y cuando ves let mut, es una señal clara: “ojo con esta, va a cambiar”.
Además atrapa errores reales. Si querías cambiar una copia pero escribiste por accidente en el original, o reutilizaste un nombre sin querer, el compilador te detiene. Y Rust también te avisa de las variables mut que nunca cambian (variable does not need to be mutable, la variable no necesita ser mutable), así que la señal es fiable en los dos sentidos.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»Solución 1: Añade mut
Sección titulada «Solución 1: Añade mut»Elige esta cuando la variable de verdad debe cambiar con el tiempo: un contador, un total acumulado, una puntuación, un estado. Es la solución correcta la mayoría de las veces, y es justo lo que sugiere el compilador.
fn main() { let mut score = 10; println!("Puntos: {score}"); score = 15; // ✅ ahora sí se puede println!("Nuevos puntos: {score}");
let mut count = 0; for _ in 0..3 { count += 1; } println!("Contador: {count}");}Salida:
Puntos: 10Nuevos puntos: 15Contador: 3El contador es el caso clásico: let count = 0; seguido de count += 1; dentro de un bucle da E0384 en la línea del +=. Añadir mut lo arregla. Mira Hacer que una variable se pueda cambiar con mut.
Solución 2: Usa shadowing para un valor nuevo
Sección titulada «Solución 2: Usa shadowing para un valor nuevo»Elige esta cuando en realidad no quieres cambiar la variable, sino obtener un valor nuevo calculado a partir del anterior, a menudo en varios pasos, y que después ya no cambie. Escribe let otra vez con el mismo nombre:
fn main() { let price = 20; let price = price + 5; // un `price` nuevo, hecho a partir del anterior let price = price * 2; println!("Precio final: {price}");
let input = " 42 "; let input = input.trim(); // sigue siendo texto let input: u32 = input.parse().unwrap(); // ahora es un número println!("Convertido: {}", input + 1);}Salida:
Precio final: 50Convertido: 43Esto se llama shadowing (sombreado): cada let crea una variable completamente nueva que oculta a la anterior. Trae dos ventajas. El price final es inmutable, así que nada puede cambiarlo por accidente más adelante. Y el tipo puede cambiar, como input, que pasa de &str a u32, algo que mut no puede hacer. Hay más sobre esto en Shadowing: reutilizar el mismo nombre.
Una regla sencilla: si el valor cambia una y otra vez (en un bucle, con el tiempo), usa mut. Si estás preparando un valor en unos pocos pasos, el shadowing se lee muy bien.
Solución 3: Añade mut a un parámetro de función
Sección titulada «Solución 3: Añade mut a un parámetro de función»Elige esta cuando una función quiere cambiar su propia copia de un argumento. Los parámetros también son inmutables, así que esto falla:
fn countdown(n: u32) { while n > 0 { println!("{n}..."); n -= 1; // ❌ cannot assign to immutable argument `n` } println!("Liftoff!");}error[E0384]: cannot assign to immutable argument `n` --> src/main.rs:4:9 |4 | n -= 1; // ❌ cannot assign to immutable argument `n` | ^^^^^^ cannot assign to immutable argument |help: consider making this binding mutable |1 | fn countdown(mut n: u32) { | +++Pon mut delante del nombre del parámetro:
fn countdown(mut n: u32) { while n > 0 { println!("{n}..."); n -= 1; } println!("¡Despegue!");}
fn main() { let start = 3; countdown(start); println!("start sigue valiendo {start}");}Salida:
3...2...1...¡Despegue!start sigue valiendo 3Fíjate en que start, en main, no cambió. mut n solo hace modificable la propia copia de la función; quien la llama no necesita mut y no se ve afectado. Si quieres que la función cambie la variable de quien la llama, recibe un &mut u32 y pasa &mut start. Mira Funciones y Borrowing.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»Al cambiar la variable de un bucle for. La variable del bucle es una variable nueva e inmutable en cada vuelta:
for i in 0..5 { if i == 2 { i += 1; // ❌ cannot assign twice to immutable variable `i` } println!("{i}");}Solución: casi nunca quieres cambiar el propio contador del bucle. Sáltate vueltas con continue, usa .step_by(2) o, si necesitas control total, pásate a un bucle while con un let mut i. (for mut i in 0..5 compila, pero solo cambia la copia de esa vuelta; no se salta nada.) Mira break y continue y bucles while.
Al declarar primero y asignar después. let total; sin valor está permitido, y puedes asignarle un valor una vez. Una segunda asignación da E0384:
let total;total = 5;total = 6; // ❌ cannot assign twice to immutable variable `total`Solución: si de verdad cambia, usa let mut total. Si solo querías elegir un valor, calcúlalo en una sola expresión: let total = if big_order { 6 } else { 5 };. Mira if / else.
Al acumular un total. let sum = 0; seguido de sum += price; dentro de un bucle es el E0384 más común de todos. Solución: let mut sum = 0;, o prescinde de la variable y escribe let sum: u32 = prices.iter().sum();. Mira Iteradores.
Errores relacionados
Sección titulada «Errores relacionados»- E0596: cannot borrow as mutable, as it is not declared as mutable (no se puede tomar prestado como mutable). El mismo problema de “se te olvidó
mut”, pero al llamar a un método que modifica, como.push(), en vez de asignar. - E0594: cannot assign to
p.hp, aspis not declared as mutable. Lo verás cuando asignas a un campo de un struct (p.hp = 5;) o escribes a través de una referencia&. Solución:let mut p, o una referencia&mut. Mira Hacer un struct mutable. - E0502: intentaste cambiar un valor mientras estaba prestado para lectura.
- E0382: usaste un valor después de moverlo.
- 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. Escribes let lives = 3; y más tarde lives -= 1;. ¿Por qué obtienes E0384?
2. Quieres limpiar name una sola vez (quitarle los espacios y pasarlo a mayúsculas) y no volver a cambiarlo nunca. ¿Qué se lee mejor?
3. fn halve(n: u32) hace n /= 2; y da E0384. ¿Qué solución deja igual a quien llama a la función?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0384 — Rust error codes index (en inglés): la explicación oficial de este error, con un ejemplo.
- Variables and Mutability — El libro de Rust (en inglés): el capítulo sobre
let,mut, las constantes y el shadowing. - Mutability — Rust by Example (en inglés): un ejemplo corto y ejecutable de
letfrente alet mut. - Freezing — Rust by Example (en inglés): cómo el shadowing de una variable mutable con una inmutable la “congela”.