2015-05-25 11 views
5

a Rust, ottengo il seguente errore:Perché la mia struttura non vive abbastanza a lungo?

<anon>:14:9: 14:17 error: `mystruct` does not live long enough 
<anon>:14   mystruct.update(); 
        ^~~~~~~~ 
<anon>:10:5: 17:6 note: reference must be valid for the lifetime 'a as defined on the block at 10:4... 
<anon>:10  { 
<anon>:11   let initial = vec![Box::new(1), Box::new(2)]; 
<anon>:12   let mystruct = MyStruct { v : initial, p : &arg }; 
<anon>:13   
<anon>:14   mystruct.update(); 
<anon>:15   
      ... 
<anon>:12:59: 17:6 note: ...but borrowed value is only valid for the block suffix following statement 1 at 12:58 
<anon>:12   let mystruct = MyStruct { v : initial, p : &arg }; 
<anon>:13   
<anon>:14   mystruct.update(); 
<anon>:15   
<anon>:16   mystruct 
<anon>:17  } 
error: aborting due to previous error 

per il seguente codice:

struct MyStruct<'a> 
{ 
    v : Vec<Box<i32>>, 
    p : &'a i32 
} 

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 
    } 

    fn update(&'a mut self) 
    { 
     self.p = &self.v.last().unwrap(); 
    } 

} 

fn main() { 
    let x = 5; 
    let mut obj = MyStruct::new(&x); 
} 

(Playground)

non capisco il motivo per cui mystruct non vive abbastanza. Se commento la riga mystruct.update() funziona comunque. Inoltre, se commento il corpo di update, il codice non funziona ancora. Perché chiamare una funzione vuota che prende in prestito una variabile self cambia le cose?

Non capisco quale riferimento è quello di cui parla l'errore. Qualcuno può spiegarlo?

risposta

8

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

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

+0

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

+0

@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. –

Problemi correlati