Error E0597 de Rust: borrowed value does not live long enough (no vive bastante)
E0597 significa: guardaste una referencia (&x) durante más tiempo del que vive el propio valor x. El valor se limpia al final de su bloque, pero algo fuera de ese bloque todavía quiere usar la referencia. Es un error muy común, sobre todo con bloques internos, bucles y structs, y la solución suele ser mover una línea o guardar un valor propio (owned) en lugar de una referencia. (Esta página forma parte de Errores del compilador de Rust explicados.)
El mensaje de error
Sección titulada «El mensaje de error»error[E0597]: `x` does not live long enough --> src/main.rs:5:13 |4 | let x = 5; | - binding `x` declared here5 | r = &x; // r toma prestado x | ^^ borrowed value does not live long enough6 | } // aquí se libera x | - `x` dropped here while still borrowed7 | println!("r: {r}"); // ❌ r apuntaría a la nada | - borrow later used hereEl compilador te dice que x no vive lo suficiente (does not live long enough): se declaró en la línea 4, se libera en la línea 6 mientras sigue prestada, y el préstamo se usa después, en la línea 7. Este es el programa que lo provoca:
fn main() { let r; { let x = 5; r = &x; // r toma prestado x } // aquí se libera x println!("r: {r}"); // ❌ r apuntaría a la nada}Qué significa
Sección titulada «Qué significa»Las cuatro etiquetas cuentan la historia en orden:
- Línea 4:
xse crea dentro del bloque interno{ ... }. - Línea 5:
rtoma prestadox. Hasta aquí, todo bien. - Línea 6: el bloque termina, así que
xse libera (se hace drop, se limpia). El préstamo sigue vivo. - Línea 7: se usa
r, pero el valor al que apunta ya no existe.
Piensa en apuntar el número de la habitación de hotel de un amigo. Te sirve mientras tu amigo se aloje allí. Cuando se va, el número sigue en tu nota, pero ahora te lleva a una habitación vacía, o a la de un desconocido. Rust no te deja guardar una nota que lleva a una habitación vacía.
También verás la etiqueta corta “borrowed value does not live long enough” (el valor prestado no vive lo suficiente). Es el mismo error; el encabezado simplemente nombra la variable.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»Una referencia nunca debe vivir más que el valor al que apunta. Si lo hiciera, tendrías una referencia colgante (dangling reference): un puntero a memoria que ya se liberó y que ahora puede contener otra cosa. En C y C++ esa es una fuente clásica de cuelgues y fallos de seguridad (“use after free”, uso después de liberar).
En Rust, cada valor se libera cuando su dueño sale de su ámbito (scope), normalmente en la } que cierra el bloque donde se declaró. El borrow checker (el verificador de préstamos) compara dos tramos: cuánto vive el valor y cuánto tiempo se usa la referencia. Si la referencia se usa después de la } del valor, obtienes E0597. Mira Ámbito, Borrowing y Lifetimes.
Fíjate en lo que el compilador no critica: tomar prestado x en la línea 5. El problema es solo el uso posterior en la línea 7. Quita esa última línea y el programa compila. Es una pista útil para las soluciones de abajo: o haces que el valor viva más, o haces que el uso de la referencia termine antes.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»Solución 1: Mueve el valor a un ámbito exterior
Sección titulada «Solución 1: Mueve el valor a un ámbito exterior»Elige esta cuando el valor realmente se necesita mientras exista la referencia. Es la solución más común: declara el valor antes del bloque (fuera de él), para que viva al menos tanto como la referencia.
fn main() { let scores = vec![72, 95, 88]; // ahora vive hasta el final de main let best; { best = scores.iter().max().unwrap(); } println!("Mejor puntuación: {best}"); // ✅ scores sigue vivo}Salida:
Mejor puntuación: 95Regla práctica: el dueño tiene que declararse antes que quien toma prestado y vivir más que él. Si ves un valor creado dentro de un bloque o de un bucle y una referencia a él que se usa fuera, saca el valor de ahí.
Solución 2: Guarda valores propios en lugar de referencias
Sección titulada «Solución 2: Guarda valores propios en lugar de referencias»Elige esta cuando reúnes valores que se crean dentro de un bucle. Cada vuelta del bucle crea un String nuevo y lo libera al final de esa vuelta, así que un Vec<&str> fuera del bucle no puede guardar referencias a ellos:
fn main() { let mut names: Vec<&str> = Vec::new(); for i in 1..=3 { let name = format!("player{i}"); names.push(&name); // ❌ `name` does not live long enough } println!("{names:?}");}La solución es que el Vec sea el dueño de los strings. Cambia Vec<&str> por Vec<String> y añade el propio String (así se mueve dentro del vector y vive tanto como él):
fn main() { let mut names: Vec<String> = Vec::new(); for i in 1..=3 { let name = format!("jugador{i}"); names.push(name); // mueve el String dentro del Vec } println!("{names:?}");}Salida:
["jugador1", "jugador2", "jugador3"]La misma idea vale para los structs y los HashMap: si los datos se crean en un ámbito más pequeño que el contenedor, guarda String (o Vec<T>) en lugar de &str (o &[T]). Los campos propios son casi siempre la mejor opción cuando estás empezando. Mira Strings y Vectores.
Solución 3: Clónalo
Sección titulada «Solución 3: Clónalo»Elige esta cuando también necesitas seguir usando el original, o cuando solo tienes un préstamo de él. Un struct que guarda una referencia (Label<'a> { text: &'a str }) no puede vivir más que el string al que apunta. Dale al struct un campo String propio y guarda una copia:
struct Label { text: String, // propio, no &str}
fn main() { let label; { let title = String::from("Recibidos"); label = Label { text: title.clone() }; println!("Dentro del bloque: {title}"); } println!("Etiqueta: {}", label.text); // ✅ label tiene su propia copia}Salida:
Dentro del bloque: RecibidosEtiqueta: RecibidosSi después no necesitas title, sáltate el clon y muévelo: Label { text: title }. Clonar un string corto es barato y está perfectamente bien. Si solo tienes un &str, usa .to_string() para obtener una copia propia.
Solución 4: Reorganiza el código para que el préstamo termine a tiempo
Sección titulada «Solución 4: Reorganiza el código para que el préstamo termine a tiempo»Elige esta cuando el valor está pensado para durar poco. En lugar de hacer que el valor viva más, usa la referencia mientras el valor sigue vivo. Esto aparece mucho con funciones que devuelven una referencia, como el clásico ejemplo longest:
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() >= b.len() { a } else { b }}
fn main() { let first = String::from("Ferris the crab"); { let second = String::from("Corro"); let winner = longest(&first, &second); println!("Nombre más largo: {winner}"); // ✅ se usa antes de liberar second }}Salida:
Nombre más largo: Ferris the crab(Los nombres se quedan en inglés porque el resultado depende de su longitud con .len().) Si winner se declara fuera del bloque y se imprime después, obtienes E0597 para second, aunque first sea el más largo. La firma dice que el resultado puede tomar prestado de cualquiera de los dos argumentos, así que los dos tienen que vivir más que él. Si de verdad necesitas el resultado más tarde, guárdalo como valor propio: let winner = longest(&first, &second).to_string();.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»Una closure guardada fuera del bloque. Una closure que usa name lo toma prestado. Si la closure vive más que name, obtienes E0597 con una etiqueta extra, value captured here (el valor se capturó aquí):
let say_hello;{ let name = String::from("Ferris"); say_hello = || println!("Hello, {name}!"); // ❌ `name` does not live long enough}say_hello();Solución: escribe move || ... para que la closure se quede con el ownership (propiedad) de name. Mira Closures.
Hilos. Tomar prestada una variable local dentro de thread::spawn(|| ...) da otro error, E0373 (“closure may outlive the current function”, la closure puede vivir más que la función actual), porque el hilo podría seguir ejecutándose después de main. Solución: thread::spawn(move || ...), compartir con Arc, o usar std::thread::scope. Mira Concurrencia.
Una referencia a un valor temporal. let label = Label { text: String::from("Recibidos").as_str() }; se parece mucho a E0597, pero aparece como E0716, “temporary value dropped while borrowed” (valor temporal liberado mientras estaba prestado): el String nunca se guarda en una variable, así que se libera al final de la sentencia. Solución: guárdalo primero en una variable (let title = String::from("Recibidos");) o haz que el campo sea un String.
Al devolver una referencia desde una función. Si el valor se crea dentro de la función, obtienes E0515. Devuelve el valor propio.
Errores relacionados
Sección titulada «Errores relacionados»- E0515: cannot return reference to local variable (no se puede devolver una referencia a una variable local): el mismo problema, pero al salir de una función.
- E0373: closure may outlive the current function: la versión de este error con hilos. Normalmente se arregla con
move. - E0106: missing lifetime specifier (falta un especificador de lifetime): Rust necesita saber de dónde toma prestado una referencia que se devuelve o se guarda.
- E0382: borrow of moved value (uso de un valor movido): la trampa contraria, usar un valor después de haberlo entregado.
- E0502: mutable borrow while immutably borrowed: una referencia sigue en uso cuando intentas cambiar el valor.
- E0716: temporary value dropped while borrowed: como E0597, pero para un valor que nunca se guardó en una variable.
- Todos los errores del compilador explicados
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»Comprueba lo aprendido
1. En let r; { let x = 5; r = &x; } seguido de un println! de r, ¿qué provoca realmente E0597?
2. En cada vuelta de un bucle construyes un String y añades &name a un Vec<&str>. ¿Cuál es la solución habitual?
3. La closure de un hilo toma prestado un Vec local. ¿Qué error obtienes?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0597 — Rust error codes index (en inglés): la explicación oficial de este error.
- The Borrow Checker — El libro de Rust (en inglés): el mismo ejemplo con
ryx, con un diagrama de los dos lifetimes. - Borrowing — Rust by Example (en inglés): ejemplos cortos y ejecutables de préstamos y de cuánto duran.
- Lifetimes — Rust by Example (en inglés): cómo ve el compilador la vida de cada valor y de cada préstamo.
- E0716 — Rust error codes index (en inglés): el error muy parecido “temporary value dropped while borrowed”.