Ir al contenido

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.)

error[E0597]: `x` does not live long enough
--> src/main.rs:5:13
|
4 | let x = 5;
| - binding `x` declared here
5 | r = &x; // r toma prestado x
| ^^ borrowed value does not live long enough
6 | } // aquí se libera x
| - `x` dropped here while still borrowed
7 | println!("r: {r}"); // ❌ r apuntaría a la nada
| - borrow later used here

El 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
}

Las cuatro etiquetas cuentan la historia en orden:

  1. Línea 4: x se crea dentro del bloque interno { ... }.
  2. Línea 5: r toma prestado x. Hasta aquí, todo bien.
  3. Línea 6: el bloque termina, así que x se libera (se hace drop, se limpia). El préstamo sigue vivo.
  4. 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.

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.

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: 95
▶ Pruébalo tú mismo

Regla 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"]
▶ Pruébalo tú mismo

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.

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: Recibidos
Etiqueta: Recibidos
▶ Pruébalo tú mismo

Si 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
▶ Pruébalo tú mismo

(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();.

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.

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