Il riferimento a cui si riferisce questo errore è quello che viene creato implicitamente quando si chiama update()
. Perché update()
prende &'a mut self
, significa che accetta un valore di tipo &'a mut MyStruct<'a>
. Ciò significa che, in teoria, si dovrebbe chiamare update()
come questo:
(&mut mystruct).update();
Sarebbe molto scomodo a scrivere questo in tutto il mondo, e così ruggine è in grado di inserire automaticamente necessario &
s, &mut
s e *
s in modo da chiamare un metodo. Questo è chiamato "autoreference", e l'unico posto in cui si verifica è invocazioni di metodi/accesso al campo.
Il problema è la definizione di update()
metodo:
impl<'a> MyStruct<'a> {
...
fn update(&'a mut self) { ... }
...
}
Qui si richiede che update()
riceve il valore è chiamato a attraverso un riferimento con tempo di vita 'a
, dove 'a
è la durata del riferimento memorizzato in la struttura.
Tuttavia, quando si sta chiamando questo metodo su un valore di struttura, deve essere già presente un riferimento a i32
memorizzato in questa struttura. Quindi la durata del valore della struttura è strettamente inferiore alla durata indicata dal parametro lifetime, quindi è semplicemente impossibile costruire &'a mut MyStruct<'a>
con variabili locali (come nel tuo caso).
La soluzione è quella di utilizzare &mut self
anziché &'a mut self
:
fn update(&mut self) { ... }
// essentially equivalent to
fn update<'b>(&'b mut self) where 'a: 'b { ... }
// `'b` is a fresh local lifetime parameter
questo modo la durata della struttura in questo metodo chiamata non è legata al riferimento questa struttura contiene e può essere più piccolo.
Di seguito viene fornita una spiegazione più approfondita.
Di per sé la definizione non è una sciocchezza. Ad esempio:
struct IntRefWrapper<'a> {
value: &'a i32
}
static X: i32 = 12345;
static Y: IntRefWrapper<'static> = IntRefWrapper { value: &X };
impl<'a> IntRefWrapper<'a> {
fn update(&'a self) { ... }
}
Y.update();
Qui update()
invocazione non causerà errori di compilazione perché entrambi lifetimes (di Y
e di X
, riferimento al quale è contenuto in Y
) sono 'static
.
Consideriamo il tuo esempio, per il confronto:
impl<'a> MyStruct<'a> {
fn new(arg : &'a i32) -> MyStruct<'a> {
let initial = vec![Box::new(1), Box::new(2)];
let mystruct = MyStruct { v : initial, p : &arg };
mystruct.update();
mystruct
}
}
qui abbiamo un parametro di vita, 'a
, che viene fornito dal chiamante della funzione. Ad esempio, il chiamante potrebbe chiamare questa funzione con un riferimento statico:
static X: i32 = 12345;
MyStruct::new(&X); // here &X has static lifetime
Tuttavia, quando update()
metodo viene richiamato, mystruct
durata è delimitata dal blocco viene chiamato in:
{
let initial = vec![Box::new(1), Box::new(2)];
let mystruct = MyStruct { v : initial, p : &arg }; // +
// |
mystruct.update(); // |
// |
mystruct // |
}
Naturalmente, il controllore del prestito non può dimostrare che questa vita sia la stessa durata fornita dal chiamante (e per ogni possibile "vita" esterna è impossibile per loro coincidere), quindi genera un errore.
Quando l'aggiornamento è definito in questo modo:
fn update(&mut self) { ... }
// or, equivalently
fn update<'b>(&'b mut self) where 'a: 'b { ... }
poi quando lo si chiama, non è più necessario che il valore si chiama questo metodo su deve vivere esattamente il tempo che 'a
- è sufficiente che esso per vivere per tutta la vita che è inferiore o uguale a 'a
- e la durata della funzione corrisponde perfettamente a questi requisiti. Quindi puoi chiamare tale metodo sul tuo valore, e il compilatore non si lamenterà.
Inoltre (come notato nei commenti) la seguente riga è davvero valido e non c'è modo intorno ad esso:
self.p = &self.v.last().unwrap();
Il controllo prestito fallisce qui perché si sta cercando di memorizzare un riferimento con la vita di la struttura nella struttura stessa. In generale questo non può essere fatto perché ha problemi di solidità. Ad esempio, supponiamo di essere effettivamente in grado di memorizzare questo riferimento nella struttura. Ma ora non è possibile modificare la struttura Vec<Box<i32>>
perché potrebbe distruggere un elemento a cui fanno riferimento i riferimenti precedentemente memorizzati, rendendo la memoria del codice non sicura.
È impossibile controllare queste cose in modo statico, pertanto non è consentito il controllo del prestito. In realtà, è solo una bella conseguenza delle regole generali di controllo dei prestiti.
1) Penso che ti sbagli sui limiti di durata: la sintassi è ''lunga:' breve' (per ' 'long' outlives' 'short'). Quindi, dovrebbe essere scritto 'fn update <'b> (& 'b mut self) dove' a: 'b {...}' http://is.gd/EEKFnB – mdup
2) Non indirizza la linea ' self.p = & self.v.last(). unwrap(); 'che non riesce a compilare . La spiegazione è che il riferimento preso proprio a questa riga non vive oltre il blocco in 'update()', che il verificatore di prestiti rifiuta correttamente. http://is.gd/mlhHvb – mdup
@mdup, in effetti, hai ragione riguardo alla sintassi delle durate. In qualche modo ho pensato che fosse il contrario. ''a:' b' dovrebbe essere letto come" ''a' è almeno lungo come' 'b'", quindi tutto inizia a dare un senso. –