← Pergaminos rust

Los tiempos de vida en Rust - no una duración, sino una dependencia

La anotación `'a` no controla cuánto tiempo viven los datos - declara una dependencia: la referencia es válida mientras ambos argumentos están vivos

Esta traducción fue asistida por IA y revisada superficialmente. Si encuentras errores o algo no suena natural, escríbeme - lo agradezco.

Has escrito funciones que reciben referencias. En algún momento intentaste devolver una - y Rust te detuvo con un error sobre especificadores de tiempo de vida. Añadiste 'a a la firma, funcionó o no funcionó, y en cualquier caso no sabías por qué. La conclusión más habitual en ese punto es que 'a controla cuánto tiempo vive algo. No es así.

El contrato de alquiler

Imagina que organizas un evento. Has alquilado una sala desde las siete hasta medianoche. Bajo ese contrato emites pases de acceso. Cada pase es válido exactamente durante el tiempo de la reserva - no más. Cuando la reserva termina y la sala se devuelve al propietario, todos los pases pierden su validez: el responsable del local no los aceptará. El contrato no dice cuánto tiempo existe el edificio; dice cuánto tiempo dura tu derecho sobre él. Los pases no prolongan la reserva. Solo hacen referencia a ella.

Ahora imagina dos salas - una principal y una de reserva. Ambas están disponibles desde el inicio del evento, pero la principal está alquilada hasta las diez y la de reserva hasta medianoche. Cuál usar depende de la afluencia de público. Emites un pase para el equipo de catering. Ese pase debe funcionar independientemente de qué sala se acabe eligiendo - lo que significa que solo es válido mientras ambas opciones siguen abiertas, es decir, hasta las diez. No porque esté escrito así en el pase, sino porque es el único periodo en que el resultado está garantizado.

Eso es lo que hace la anotación 'a en Rust: declara que la referencia devuelta es válida exactamente mientras ambos argumentos están vivos. No más.

La fecha en el pase

Un error frecuente: 'a es una duración, y escribirla le dice a Rust que mantenga algo vivo más tiempo. Este modelo es incorrecto. Persiste porque añadir 'a a veces hace desaparecer el error del compilador - y eso parece una confirmación. Es una coincidencia, no una causa.

fn active_label(current: &str, default: &str) -> &str {
    if current.is_empty() { default } else { current }
}
error[E0106]: missing lifetime specifier
 --> src/main.rs:1:46
  |
1 | fn active_label(current: &str, default: &str) -> &str {
  |                          ----           ----     ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature
    does not say whether it is borrowed from `current` or `default`

El compilador no pregunta cuánto tiempo debe vivir algo. Pregunta: ¿hasta cuándo es válido este pase? El &str devuelto apunta a datos de uno de los dos argumentos - pero sin la anotación el compilador no sabe de cuál, y por tanto no puede determinar la validez del pase ni verificar que no caduque antes de ser utilizado.

La marca en el pase

fn active_label<'a>(
    current: &'a str,  // sala principal - hasta las diez
    default: &'a str,  // sala de reserva - hasta medianoche
) -> &'a str {         // pase válido mientras ambas estén abiertas
    if current.is_empty() { default } else { current }
}

'a no prolonga la vida de current ni de default. Es una declaración: la referencia devuelta es válida dentro de la intersección de sus duraciones - mientras ambos estén vivos. La duración real la determinan los ámbitos en el lugar de la llamada.

Ahora el compilador puede verificarlo:

fn main() {
    let label;
    {
        let current = String::from("beta");          // sala principal abre
        label = active_label(&current, "stable");    // pase emitido - válido mientras ambas abiertas
    }                                                // sala principal cerró - pase caducado
    println!("{}", label);                           // pase ya no es válido
}
error[E0597]: `current` does not live long enough
  --> src/main.rs:9:28
   |
9  |         label = active_label(&current, "stable");
   |                              ^^^^^^^^ borrowed value does not live long enough
10 |     }
   |     - `current` dropped here while still borrowed
11 |     println!("{}", label);
   |                    ----- borrow later used here

La anotación hizo visible la dependencia. El compilador encontró la infracción. La solución no es cambiar la anotación - es usar label mientras ambas salas siguen abiertas:

fn main() {
    let current = String::from("beta");           // sala principal abre
    let label = active_label(&current, "stable"); // pase emitido - ambas salas aún abiertas
    println!("{}", label);                        // usado mientras ambas siguen abiertas
}                                                 // salas cierran - pase caduca, pero ya no se necesita

Lo que ahora lees de otra manera

Cuando veas 'a en una firma de función, deja de leerlo como un temporizador. Léelo como una condición: la referencia devuelta es válida mientras ambos argumentos están vivos.


Cabe saber una cosa más: si necesitas devolver una referencia de un único argumento concreto, puedes darles anotaciones distintas - 'a y 'b. La referencia devuelta queda entonces vinculada exactamente al argumento del que depende. Eso es un control preciso, no una intersección.