rust lifetime

rust - Anotación de por vida para el argumento de cierre



lifetime (2)

Me gustaría hacer la siguiente compilación de código:

struct Provider {} impl Provider { fn get_string<''a>(&''a self) -> &''a str { "this is a string" } } fn main() { let provider = Provider{}; let mut vec: Vec<&str> = Vec::new(); // PROBLEM: how do I say that this reference s here // needs to live as long as vec? let fun = |s: &str| { vec.push(s); }; fun(provider.get_string()); }

Enlace de juegos

Este es el error de compilación que obtengo:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements --> src/main.rs:9:22 | 9 | let mut vec: Vec<&str> = Vec::new(); | ^^^^ | note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 11:24... --> src/main.rs:11:25 | 11| let fun = |s: &str| { | ^ note: ...so that reference does not outlive borrowed content --> src/main.rs:12:18 | 12| vec.push(s); | ^ note: but, the lifetime must be valid for the block suffix following statement 2 at 13:6... --> src/main.rs:13:7 | 13| }; | ^ note: ...so that variable is valid at time of its declaration --> src/main.rs:11:9 | 11| let fun = |s: &str| { | ^^^


Su código funciona bien si elimina todas las anotaciones de por vida y deja que la inferencia del compilador haga su trabajo:

struct Provider; impl Provider { fn get_string(&self) -> &str { "this is a string" } } fn main() { let provider = Provider; let mut vec = Vec::new(); let mut fun = |s| { vec.push(s); }; fun(provider.get_string()); }

En resumen, no hay forma de referirse explícitamente a la duración de una variable local, solo argumentos de funciones. El compilador sabe cómo hacerlo, sin embargo.

Si realmente lo necesita, puede crear una función para permitir anotar las vidas:

fn thing<''a>(provider: &''a Provider) -> Vec<&''a str> { let mut vec: Vec<&''a str> = Vec::new(); { let mut fun = |s: &''a str| vec.push(s); fun(provider.get_string()); } // End mutable borrow of `vec` vec } fn main() { let provider = Provider; thing(&provider); }

¿Por qué las anotaciones originales impiden que las cosas funcionen?

Específicamente, es este bit:

let fun = |s: &str| { vec.push(s); };

Esto declara una nueva vida en el cierre. El uso de una sintaxis inventada (no se pueden declarar tiempos de vida en los argumentos de cierre ) sería equivalente a:

let fun = <''a> |s: &''a str| { vec.push(s); };

Por eso el compilador tiene el error:

la duración no puede sobrevivir al tiempo de vida n. ° 1 definido en [el bloqueo del cierre]

No hay conexión entre esa vida generada y la del Provider . Salir de él permite al compilador insertar el tiempo de vida deseado pero innombrable.


Aquí hay una versión que compila:

use std::marker::PhantomData; struct Provider<''a> { _dummy: PhantomData<&''a ()>, } impl<''a> Provider<''a> { fn get_string(&self) -> &''a str { "this is a string" } } fn f<''b>() { let provider = Provider { _dummy: PhantomData }; let mut vec: Vec<&str> = Vec::new(); // PROBLEM: how do I say that this reference s here // needs to live as long as vec? let mut fun = |s: &''b str| { vec.push(s); }; fun(provider.get_string()); } fn main() { f() }

Enlace de juegos

Hice los siguientes cambios:

  • Agregue una vida útil al Provider (agregué un PhantomData , pero supongo que su proveedor ya posee algunos datos que proporcionará).
  • Actualice el método get_string para mostrar que devuelve algo con la duración del proveedor, no la duración de la entrada (es decir, según el parámetro de por vida del Provider ).
  • Agregue un nuevo parámetro de por vida ''b a la función (que renombré a f() , ya que main() no puede tener uno), que utilizo para nombrar el tiempo de vida del parámetro de cierre.

El último es un poco confuso, ya que aparentemente simplemente agregar un nombre a una vida (sin agregar ninguna restricción) lo ha hecho funcionar.

Creo (pero me gustaría algo de documentación para esto) que esto se debe a la elisión de por vida. Un cierre es realmente una struct oculta con un método fn call(&self, s: &str) (en este caso). De acuerdo con las reglas de elisión de por vida , el parámetro s gana la misma duración que &self , que es el cierre en sí mismo. En este caso, el cierre se declara después de vec , por lo que la vida útil es demasiado corta. La vida explícita significa que está desacoplada de la propia vida del cierre.