Saltar al contenido principal
Rust

Rust: cannot borrow as mutable because it is also borrowed as immutable

Cómo entender y resolver los errores del borrow checker de Rust. Guía práctica para E0502, E0505, E0506 y los patrones para superarlos.

Error: error[E0502]: cannot borrow as mutable because it is also borrowed as immutable

¿Por qué ocurre?

El borrow checker de Rust garantiza memory safety en tiempo de compilación. El error ocurre porque las reglas de borrowing prohíben: - Tener una referencia mutable (`&mut`) mientras existen referencias inmutables (`&`) al mismo dato - Tener más de una referencia mutable al mismo dato simultáneamente - Usar un valor después de haberlo movido (moved value) - Devolver referencias a datos locales que se liberan al salir de la función

Solución paso a paso

1. El error clásico: mezclar & y &mut

fn main() {
    let mut v = vec![1, 2, 3];

let primer = &v[0]; // ← borrow inmutable v.push(4); // ❌ borrow mutable mientras existe el inmutable println!("{}", primer);

// ✅ Solución 1: usar el borrow inmutable antes de la mutación let primer = v[0]; // copiar el valor (si implementa Copy) v.push(4); println!("{}", primer); // ✅

// ✅ Solución 2: limitar el scope del borrow inmutable { let primer = &v[0]; println!("{}", primer); } // borrow inmutable termina aquí v.push(4); // ✅ }

2. Múltiples referencias mutables — usar RefCell o Mutex

use std::cell::RefCell;

// RefCell: mutabilidad interior (single-threaded) let dato = RefCell::new(vec![1, 2, 3]);

let mut ref1 = dato.borrow_mut(); ref1.push(4); drop(ref1); // liberar antes de otro borrow

let ref2 = dato.borrow(); println!("{:?}", *ref2); // ✅

// Mutex: para multi-threading use std::sync::{Arc, Mutex};

let dato = Arc::new(Mutex::new(vec![1, 2, 3])); let mut locked = dato.lock().unwrap(); locked.push(4);

3. Valor movido (use of moved value)

// ❌ s se mueve a la función y ya no está disponible
fn tomar_ownership(s: String) {
    println!("{}", s);
}

let s = String::from("hola"); tomar_ownership(s); println!("{}", s); // ❌ error: value borrowed here after move

// ✅ Opción 1: pasar referencia fn usar_ref(s: &str) { println!("{}", s); } let s = String::from("hola"); usar_ref(&s); println!("{}", s); // ✅

// ✅ Opción 2: clonar (tiene coste de memoria) tomar_ownership(s.clone()); println!("{}", s); // ✅

4. Devolver referencias — lifetimes

// ❌ Devolver referencia a dato local
fn obtener_string() -> &str {  // ❌ missing lifetime specifier
    let s = String::from("hola");
    &s // ← s se libera al salir de la función
}

// ✅ Devolver el String directamente (ownership) fn obtener_string() -> String { String::from("hola") }

// ✅ Lifetime explícito cuando devuelves referencia a un parámetro fn mas_larga<'a>(s1: &'a str, s2: &'a str) -> &'a str { if s1.len() > s2.len() { s1 } else { s2 } }

5. Patrones idiomáticos para evitar conflictos

// ✅ Indexar y modificar: usar índice numérico
let mut v = vec![1, 2, 3];
let len = v.len();           // obtener antes del borrow mutable
for i in 0..len {
    v[i] *= 2;
}

// ✅ Usar entry API en HashMap para evitar double borrow use std::collections::HashMap; let mut mapa: HashMap = HashMap::new();

mapa.entry("clave".to_string()) .and_modify(|v| *v += 1) .or_insert(1);

Cómo evitarlo en el futuro

- Lee los mensajes de error del compilador completos — Rust tiene los mejores mensajes de error de cualquier lenguaje - Usa `cargo clippy` para sugerencias de código idiomático - Aprende los patrones: `clone()` para simplificar, luego optimiza con referencias - `RefCell` para mutabilidad interior en single-thread; `Arc<Mutex<T>>` para multi-thread - El libro oficial "The Rust Programming Language" (gratis online) explica el borrow checker en detalle

Rustborrow checkerownershiplifetimesmemoria

¿Quieres que una IA te ayude? Genera el prompt perfecto para tu error:

Generador de Prompts

¿Necesitas desarrollo a medida?

Apps web, IA, módulos ERP — cuéntame tu proyecto.