Rust
Lifetimes
Ricerca…
Sintassi
- Funzione fn <'a> (x: &' a Type)
- struct Struct <'a> {x: &' a Type}
- enum Enum <'a> {Variant (&' a Type)}
- impl <'a> Struct <' a> {fn x <'a> (& self) -> &' a Type {self.x}}
- impl <'a> Tratto <' a> per Tipo
- impl <'a> Tratto per Tipo <' a>
-
fn function<F>(f: F) where for<'a> F: FnOnce(&'a Type)
-
struct Struct<F> where for<'a> F: FnOnce(&'a Type) { x: F }
-
enum Enum<F> where for<'a> F: FnOnce(&'a Type) { Variant(F) }
-
impl<F> Struct<F> where for<'a> F: FnOnce(&'a Type) { fn x(&self) -> &F { &self.x } }
Osservazioni
- Tutti i riferimenti in Rust hanno una durata, anche se non sono esplicitamente annotati. Il compilatore è in grado di assegnare implicitamente delle durate.
- La durata
'static
è assegnata ai riferimenti che sono memorizzati nel programma binario e saranno validi per tutta la sua esecuzione. Questo lifetime è in particolare assegnato a stringhe letterali, che hanno il tipo&'static str
.
Parametri di funzione (durata dell'input)
fn foo<'a>(x: &'a u32) {
// ...
}
Questo specifica che foo
ha una durata 'a
, e il parametro x
deve avere una durata almeno di 'a
. Le durate delle funzioni vengono generalmente omesse attraverso l' elisione a vita :
fn foo(x: &u32) {
// ...
}
Nel caso in cui una funzione prende più riferimenti come parametri e restituisce un riferimento, il compilatore non può dedurre la durata del risultato attraverso l' elisione a vita .
error[E0106]: missing lifetime specifier
1 | fn foo(bar: &str, baz: &str) -> &i32 {
| ^ expected lifetime parameter
Invece, i parametri di durata dovrebbero essere specificati esplicitamente.
// Return value of `foo` is valid as long as `bar` and `baz` are alive.
fn foo<'a>(bar: &'a str, baz: &'a str) -> &'a i32 {
Le funzioni possono richiedere anche più parametri di durata.
// Return value is valid for the scope of `bar`
fn foo<'a, 'b>(bar: &'a str, baz: &'b str) -> &'a i32 {
Struct Fields
struct Struct<'a> {
x: &'a u32,
}
Questo specifica che ogni data istanza di Struct
ha una durata 'a
, e il &u32
memorizzato in x
deve avere una durata almeno di 'a
.
Impl Blocks
impl<'a> Type<'a> {
fn my_function(&self) -> &'a u32 {
self.x
}
}
Questo specifica che Type
ha lifetime 'a
, e che il riferimento restituito da my_function()
potrebbe non essere più valido dopo 'a
termina perché il Type
non esiste più per contenere self.x
Limiti dei tratti più alti
fn copy_if<F>(slice: &[i32], pred: F) -> Vec<i32>
where for<'a> F: Fn(&'a i32) -> bool
{
let mut result = vec![];
for &element in slice {
if pred(&element) {
result.push(element);
}
}
result
}
Questo specifica che il riferimento su i32 nel tratto di tratto Fn
può avere una durata qualsiasi.
Quanto segue non funziona:
fn wrong_copy_if<'a, F>(slice: &[i32], pred: F) -> Vec<i32>
where F: Fn(&'a i32) -> bool
{ // <----------------+
let mut result = vec![]; // 'a scope |
for &element in slice { // <--------+ |
if pred(&element) { // | |
result.push(element); // element's| |
} // scope | |
} // <--------+ |
result // |
} // <----------------+
Il compilatore dà il seguente errore:
error: `element` does not live long enough
if pred(&element) { // | |
^~~~~~~
perché la variabile locale element
non vive fino a 'a
vita (come possiamo vedere dai commenti del codice).
La vita non può essere dichiarata a livello di funzione, perché abbiamo bisogno di un'altra vita. È per questo che abbiamo usato for<'a>
: per specificare che il riferimento può essere valido per qualsiasi durata (quindi è possibile utilizzare una durata inferiore).
I limiti dei tratti di livello superiore possono essere utilizzati anche su struct:
struct Window<F>
where for<'a> F: FnOnce(&'a Window<F>)
{
on_close: F,
}
così come su altri oggetti.
I limiti dei tratti di rango superiore sono più comunemente usati con i tratti Fn*
.
Per questi esempi, la vita elision funziona bene, quindi non dobbiamo specificare la durata.