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(¤t, "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(¤t, "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(¤t, "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.