Error E0596 de Rust: cannot borrow as mutable, as it is not declared as mutable
E0596 significa que cierto código quiere cambiar un valor (necesita un préstamo mutable, &mut), pero tú solo le diste permiso para leerlo. Normalmente se te olvidó mut en un let, o una función recibe & cuando necesita &mut. Es un error muy común, y la línea help: del compilador casi siempre te dice exactamente dónde añadir mut. En esta página verás primero la solución y después la explicación. 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[E0596]: cannot borrow `tasks` as mutable, as it is not declared as mutable --> src/main.rs:3:5 |3 | tasks.push("write tests"); // ❌ push necesita cambiar `tasks` | ^^^^^ cannot borrow as mutable |help: consider changing this to be mutable |2 | let mut tasks = Vec::new(); | +++El compilador te dice que no puede tomar prestado tasks como mutable porque no se declaró como mutable, y te sugiere cambiarlo. Este es el programa que lo provoca:
fn main() { let tasks = Vec::new(); tasks.push("write tests"); // ❌ push necesita cambiar `tasks` println!("{tasks:?}");}También verás un segundo texto: “cannot borrow *tasks as mutable, as it is behind a & reference” (no se puede tomar prestado como mutable porque está detrás de una referencia &). Es el mismo código de error con una causa algo distinta: ahí la variable es una referencia &, así que no puedes cambiar aquello a lo que apunta. Más abajo verás los dos casos.
Qué significa
Sección titulada «Qué significa»Mira los ^^^^^ debajo de tasks en la línea 3. Para añadir un elemento, Vec::push necesita un préstamo mutable del vector: su firma es fn push(&mut self, value: T). Pero tasks se creó con un let normal, así que es de solo lectura. Rust no puede darte un &mut de algo que dijiste que nunca iba a cambiar.
La línea help: muestra la solución: let mut tasks. Los +++ marcan los tres caracteres que hay que añadir.
Piensa en un documento compartido con acceso de “solo lectura”. Puedes leer cada palabra, pero el botón de “editar” está desactivado. mut (o &mut) es la forma de pedir permiso de edición.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»En Rust, las variables son inmutables por defecto, y las referencias son de solo lectura por defecto. Para cambiar algo tienes que pedirlo de forma explícita: let mut para una variable que es tuya y &mut para una referencia a algo que tomaste prestado. Mira Variables y Borrowing.
Esto hace que el código sea más fácil de entender y de confiar. Si una función recibe &Vec<String>, sabes solo por su firma que no va a cambiar tu lista. Si recibe &mut Vec<String>, sabes que puede hacerlo. Nada cambia a tus espaldas.
Esta misma regla hace posibles las garantías de seguridad más grandes de Rust. Como “puede cambiar” siempre queda escrito, el compilador puede asegurarse de que nunca haya alguien escribiendo y alguien leyendo a la vez (de eso tratan E0502 y E0499). E0596 es el primer paso: tienes que pedir permiso para escribir antes de que nadie compruebe si escribir es seguro.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»Solución 1: Añade mut a la variable
Sección titulada «Solución 1: Añade mut a la variable»Elige esta cuando el valor es tuyo y debe cambiar: un Vec al que le añades elementos, un String al que le agregas texto, o un struct al que le llamas un método con &mut self. Es la solución correcta la mayoría de las veces.
struct Counter { count: u32,}
impl Counter { fn increment(&mut self) { self.count += 1; }}
fn main() { let mut tasks = Vec::new(); tasks.push("escribir tests"); tasks.push("arreglar bug"); println!("{tasks:?}");
let mut counter = Counter { count: 0 }; counter.increment(); counter.increment(); println!("Contador: {}", counter.count);}Salida:
["escribir tests", "arreglar bug"]Contador: 2Sin mut en counter, counter.increment() falla con cannot borrow counter as mutable. Llamar a un método que recibe &mut self toma prestada toda la variable de forma mutable, así que la variable tiene que ser mut. Los métodos que solo reciben &self funcionan bien sin él. Mira Añadir comportamiento con métodos.
Lo mismo pasa con un parámetro que pertenece a la función: fn shout(s: String) no puede llamar a s.push('!'), pero fn shout(mut s: String) sí.
Solución 2: Cambia el parámetro a &mut y pasa &mut x
Sección titulada «Solución 2: Cambia el parámetro a &mut y pasa &mut x»Elige esta cuando una función necesita cambiar algo que pertenece a quien la llama. Esta versión da el texto “behind a & reference”:
fn add_task(tasks: &Vec<String>, task: &str) { tasks.push(task.to_string()); // ❌ `tasks` es una referencia `&`}error[E0596]: cannot borrow `*tasks` as mutable, as it is behind a `&` reference --> src/main.rs:2:5 |2 | tasks.push(task.to_string()); // ❌ `tasks` es una referencia `&` | ^^^^^ `tasks` is a `&` reference, so it cannot be borrowed as mutable |help: consider changing this to be a mutable reference |1 | fn add_task(tasks: &mut Vec<String>, task: &str) { | +++El compilador te explica que tasks es una referencia & y por eso no se puede tomar prestada como mutable, y te propone convertirla en una referencia mutable. Arréglalo en tres sitios: el tipo del parámetro pasa a ser &mut Vec<String>, la variable de quien llama pasa a ser let mut, y la llamada pasa &mut tasks:
fn add_task(tasks: &mut Vec<String>, task: &str) { tasks.push(task.to_string());}
fn main() { let mut tasks = Vec::new(); add_task(&mut tasks, "escribir tests"); add_task(&mut tasks, "actualizar docs"); println!("{} tareas: {tasks:?}", tasks.len());}Salida:
2 tareas: ["escribir tests", "actualizar docs"]Si cambias el parámetro pero sigues pasando &tasks, obtienes E0308 con la nota types differ in mutability (los tipos difieren en mutabilidad). Eso solo significa: escribe también &mut tasks en la llamada. Escribir &mut en la llamada es a propósito; así, cualquiera que lea main ve que add_task puede cambiar tasks.
Solución 3: Haz mut la variable de la closure
Sección titulada «Solución 3: Haz mut la variable de la closure»Elige esta cuando una closure cambia una variable que capturó. Una closure así cambia su propio estado cada vez que se ejecuta, así que para llamarla hace falta un préstamo mutable de la propia closure:
fn main() { let mut clicks = 0; let click = || clicks += 1; click(); // ❌ cannot borrow `click` as mutable click(); println!("Clicks: {clicks}");}error[E0596]: cannot borrow `click` as mutable, as it is not declared as mutable --> src/main.rs:3:9 |3 | let click = || clicks += 1; | ^^^^^ ------ calling `click` requires mutable binding due to mutable borrow of `clicks` | | | not mutable4 | click(); // ❌ cannot borrow `click` as mutable | ----- cannot borrow as mutable5 | click(); | ----- cannot borrow as mutable |help: consider changing this to be mutable |3 | let mut click = || clicks += 1; | +++El compilador explica que llamar a click requiere que sea mutable, porque toma prestado clicks de forma mutable. La variable clicks ya es mut. Lo que falta es mut en la closure:
fn main() { let mut clicks = 0; let mut click = || clicks += 1; click(); click(); click(); println!("Clics: {clicks}");}Salida:
Clics: 3Esto sorprende a mucha gente, porque la closure no “parece” que cambie. Pero guarda dentro un &mut clicks, así que es una closure FnMut, y para llamar a una FnMut hace falta mut. Mira Closures que modifican las variables capturadas y Fn, FnMut y FnOnce.
Solución 4: Usa iter_mut() o &mut en un bucle
Sección titulada «Solución 4: Usa iter_mut() o &mut en un bucle»Elige esta cuando recorres una colección y quieres cambiar cada elemento. .iter() y for x in &list te dan referencias &, que son de solo lectura:
for name in names.iter() { name.push('!'); // ❌ cannot borrow `*name` as mutable, as it is behind a `&` reference}Cámbialo por .iter_mut() o &mut names (y haz que names también sea mut):
fn main() { let mut names = vec![String::from("ferris"), String::from("corro")]; for name in names.iter_mut() { name.push('!'); } println!("{names:?}");
let mut prices = vec![10, 20, 30]; for price in &mut prices { *price += 5; } println!("{prices:?}");}Salida:
["ferris!", "corro!"][15, 25, 35]El compilador también lo sugiere: help: use mutable method ... names.iter_mut(). Lo mismo vale para las closures en cadenas de iteradores: words.iter().for_each(|w| w.push('!')) falla y words.iter_mut().for_each(|w| w.push('!')) funciona. Mira Iteradores.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»Al construir un Vec o un String paso a paso. let list = Vec::new(); y luego list.push(...), o let s = String::new(); y luego s.push_str(...). Solución: let mut. Si tienes todos los elementos desde el principio, let list = vec![a, b, c]; no necesita mut en absoluto.
Al llamar a un método “setter” o de actualización de un struct. account.deposit(50), cuando deposit recibe &mut self, falla a menos que hayas escrito let mut account. Solución: let mut account = Account::new();. Mira Structs.
Funciones auxiliares que reciben & por costumbre. Escribes fn reset(board: &Board) y luego intentas cambiar board dentro. Solución: cámbialo a board: &mut Board y llama a reset(&mut board). Un buen hábito: escribe & cuando la función solo lee y &mut cuando cambia algo.
Al cambiar un valor dentro de un HashMap. if let Some(v) = map.get("key") { v.push(..) } falla, porque get devuelve una referencia &. Solución: usa map.get_mut("key") o la API entry. Mira HashMaps.
Errores relacionados
Sección titulada «Errores relacionados»- E0384: cannot assign twice to immutable variable (no se puede asignar dos veces a una variable inmutable). El mismo
mutolvidado, pero parax = ...ox += 1en lugar de una llamada a un método. - E0594: no se puede asignar a un campo o a través de una referencia
&, por ejemplop.hp = 5;cuandopno esmut, o*x += 1enfor x in &v. Las mismas soluciones:let muto&mut. - E0308: mismatched types,
types differ in mutability(tipos que no coinciden), cuando pasas&xa una función que espera&mut x. - E0502: conseguiste acceso
&mut, pero otra cosa sigue leyendo el valor. - E0499: dos préstamos
&mutdel mismo valor al mismo tiempo. - 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. let scores = Vec::new(); scores.push(10); da E0596. ¿Cuál es la solución?
2. Una función fn add(list: &Vec<u32>) llama a list.push(1). ¿Qué cambias?
3. let mut total = 0; let add = |x| total += x; add(5); falla. ¿Por qué?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0596 — Rust error codes index (en inglés): la explicación oficial de este error.
- References and Borrowing — El libro de Rust (en inglés): la parte “Mutable References” explica
&muty por qué hay que pedirlo. - Method Syntax — El libro de Rust (en inglés):
&selffrente a&mut self, y qué necesita cada uno de quien llama. - Closures — El libro de Rust (en inglés): cómo capturan variables las closures y por qué una closure que cambia algo tiene que ser
mut. - Mutability (borrowing) — Rust by Example (en inglés): ejemplos ejecutables de préstamos
&y&mutlado a lado.