Introducción: El momento en que Rust «no te entiende»
\nEstás escribiendo en Rust. Todo va bien. El compilador calla. Te sientes el amo de la memoria. Y entonces, ¡pum! Aparece ella:
\nerror[E0507]: cannot move out of borrowed content\n --> src/main.rs:10:9\n |\n10 | let value = self.data;\n | ^^^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait\n\nO su pariente cercana:
\nerror[E0507]: cannot move out of `*self` which is behind a shared reference\n\nSi eres principiante, parece una maldición mágica. Si eres experimentado, sabes que es solo una lección más del Borrow Checker (verificador de préstamos).
\nEn este artículo analizaremos qué significan estos errores, por qué Rust es tan estricto y, lo más importante, cómo solucionarlos con ejemplos de código reales. No es teoría aburrida, sino una guía práctica para sobrevivir en el mundo de la propiedad y el préstamo.
\n\nResumen de lo esencial: Propiedad, préstamo y «movimiento»
\nAntes de meternos en detalle, recordemos los tres pilares de Rust:
\n- \n
- Propiedad (Ownership): Cada valor en Rust tiene exactamente un dueño. \n
- Préstamo (Borrowing): Puedes «tomar prestado» un valor mediante una referencia (&T — inmutable, &mut T — mutable), sin tomar la propiedad. \n
- Movimiento (Move): Cuando asignas un valor a una nueva variable o lo pasas a una función sin &, la propiedad se mueve. La variable original deja de ser válida. \n
El error cannot move out of borrowed content ocurre precisamente cuando intentas mover un valor del cual solo tienes una referencia (&T). Rust lo prohíbe porque después del movimiento la referencia se convertiría en un «puntero colgante» (dangling pointer). Y Rust, como sabes, no perdona esas cosas.
Escenario 1: Intento de mover un campo de una estructura a través de &self
\nEl caso más común. Tienes una estructura y quieres devolver uno de sus campos desde un método que recibe &self.
Código con error
\nstruct User {\n name: String,\n age: u32,\n}\n\nimpl User {\n // Intentamos entregar name "para siempre"\n fn give_name(&self) -> String {\n self.name // ¡ERROR!\n }\n}\n\nfn main() {\n let user = User { name: "Alice".to_string(), age: 30 };\n let name = user.give_name();\n println!("{}", name);\n}\n\nEl compilador dirá:
\nerror[E0507]: cannot move out of `self.name` which is behind a shared reference\n\n¿Por qué? self es &User. No somos dueños de self, solo lo hemos tomado prestado. Intentar mover self.name (tipo String, que no implementa Copy) significa que después de la llamada al método, el dueño (user) perdería su campo. ¡Pero la referencia &self todavía existe! Rust nos protege de una catástrofe.
¿Cómo solucionarlo?
\nHay tres caminos principales, dependiendo de tu tarea.
\n\nForma 1: Clonación (la más simple, pero no siempre eficiente)
\nSi necesitas exactamente el valor, no una referencia, y estás dispuesto a pagar por la copia:
\nfn give_name(&self) -> String {\n self.name.clone() // ¡Funciona! Creamos una copia.\n}\n\nInconveniente: clone() para String hace una copia completa de los datos en el heap. En cadenas grandes es costoso.
Forma 2: Devolver una referencia (cambiamos la firma)
\nSi al código que llama le basta con una referencia al campo:
\nfn give_name(&self) -> &String {\n &self.name // Devolvemos una referencia, no hay movimiento\n}\n\nAhora no movemos, sino que prestamos. Todo correcto. El código que llama recibirá un