2015-05-06 10 views
5

Cercando di compilazione:Perché unwrap_or() mantiene il prestito in ambito?

fn main() { 
    let mut vec = vec![1, 2, 3]; 
    let four: &mut u32 = borrow_or_add(&mut vec, 4); 
} 

fn borrow_or_add(vec: &mut Vec<u32>, val: u32) -> &mut u32 { 
    vec.iter_mut().find(|v| **v == val).unwrap_or({ 
     vec.push(val); 

     vec.last_mut().unwrap() 
    }) 
} 

Playground

... ha pronunciato la seguente risultato:

q.rs:8:9: 8:12 error: cannot borrow `*vec` as mutable more than once at a time 
q.rs:8   vec.push(val); 
       ^~~ 
q.rs:7:5: 7:8 note: previous borrow of `*vec` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*vec` until the borrow ends 
q.rs:7  vec.iter_mut().find(|v| **v == val).unwrap_or({ 
      ^~~ 
q.rs:12:2: 12:2 note: previous borrow ends here 
q.rs:6 fn borrow_or_add(vec: &mut Vec<u32>, val: u32) -> &mut u32 { 
... 
q.rs:12 } 
     ^
q.rs:10:9: 10:12 error: cannot borrow `*vec` as mutable more than once at a time 
q.rs:10   vec.last_mut().unwrap() 
       ^~~ 
q.rs:7:5: 7:8 note: previous borrow of `*vec` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*vec` until the borrow ends 
q.rs:7  vec.iter_mut().find(|v| **v == val).unwrap_or({ 
      ^~~ 
q.rs:12:2: 12:2 note: previous borrow ends here 
q.rs:6 fn borrow_or_add(vec: &mut Vec<u32>, val: u32) -> &mut u32 { 
... 
q.rs:12 } 
     ^
q.rs:10:9: 10:12 error: cannot borrow `*vec` as mutable more than once at a time 
q.rs:10   vec.last_mut().unwrap() 
       ^~~ 
q.rs:7:5: 7:8 note: previous borrow of `*vec` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*vec` until the borrow ends 
q.rs:7  vec.iter_mut().find(|v| **v == val).unwrap_or({ 
      ^~~ 
q.rs:12:2: 12:2 note: previous borrow ends here 
q.rs:6 fn borrow_or_add(vec: &mut Vec<u32>, val: u32) -> &mut u32 { 
... 
q.rs:12 } 
     ^

Per quanto ne so, il corpo di unwrap_or() non può fare riferimento al prestito citato di vec , quindi, perché non viene eliminato dall'ambito di applicazione? C'è un modo elegante per implementare questa funzione? In realtà non riesco a trovare un modo per eseguire correttamente questa operazione senza utilizzare due passaggi e uno bool (ad esempio).

risposta

7

Il prestito è attivo almeno per l'intera dichiarazione. Sfortunatamente, la solita soluzione di estrarre lo Option in un'istruzione separata non aiuterà, poiché conterrà ancora un riferimento mutabile (in Some) che costringe anche il prestito del vettore ad espandersi. Ciò sembra inevitabile quando si utilizza find(). Il compilatore è preoccupato che un puntatore nella memoria del vettore fluttui da qualche parte e possa essere invalidato spingendo sul vettore (che potrebbe causare una riallocazione). Ciò include non solo il puntatore nel reso Option ma anche quelli che potrebbero essere resi internamente da una di quelle funzioni. Questa sarebbe una preoccupazione abbastanza valida, se non per il fatto che non ci sono puntatori "nascosti" e si spinge solo quando il Optionnon è un puntatore. Il sistema a vita non è abbastanza dettagliato per catturare questo fatto, e non vedo un modo semplice per ripararlo o aggirarlo.

Che cosa si può fare è utilizzare position di trovare un indice:

fn borrow_or_add(vec: &mut Vec<u32>, val: u32) -> &mut u32 { 
    match vec.iter().position(|&v| v == val) { 
     Some(i) => &mut vec[i], 
     None => { 
      vec.push(val); 
      vec.last_mut().unwrap() 
     } 
    } 
} 

Nota che gli indici in vettori sono puntatori essenzialmente glorificato. Possono iniziare a riferirsi all'elemento sbagliato se il vettore cambia, sebbene non possano diventare penzolanti in caso di riallocazione e gli indici non validi (maggiori del più grande indice valido) causeranno un panico piuttosto che una mancanza di memoria. Tuttavia, ottenere l'indice da position e quindi indicizzare con esso è solo corretto perché il vettore non è (sostanzialmente) modificato in mezzo.

0

Il problema è che si sa, in virtù della sua semantica, che vec.iter_mut().find().unwrap_or() ignoreranno nulla del vec da cui proviene. Tuttavia, il compilatore non ne è a conoscenza. In effetti, la catena di chiamate al metodo potrebbe memorizzare molto bene un valore proveniente dall'iteratore, fino alla chiusura invocata da unwrap_or(). Vedere questo scenario comune, che ha una struttura analogico al tuo esempio:

let mut vec: Vec<i32> = vec![3, 2, 5]; 
vec.iter().map(|&i| { 
    vec.push(i+1) 
}).last(); 

Qui vec.iter_mut().find().unwrap_or() è solo sostituito dal vec.iter().map(). E come puoi vedere, i valori da vecpossono essere presi in prestito nella chiamata al metodo. In questo ultimo esempio è ovvio come vec.push(i+1) invalida l'iteratore, ecco perché il compilatore lo impedisce.

Problemi correlati