2015-05-16 6 views
10

Ho un RefCell<HashMap> e vogliono prendere in prestito il tavolo, trovare una chiave, e restituire un riferimento al risultato:restituendo il T preso in prestito da RefCell <T>

struct Frame { 
    map: RefCell<HashMap<String, String>> 
} 

impl Frame { 
    fn lookup<'a>(&'a self, k: &String) -> Option<&'a String> { 
     // Borrow checker barfs here! 
     self.map.borrow().get(k) 
     //^~~~~~~~~~~~~~~ borrowed value does not live long enough 
    } 
} 

(playpen)

Se rimuovo la roba RefCell poi tutto funziona bene:

struct Frame { 
    map: HashMap<String, String> 
} 

impl Frame { 
    fn lookup<'a>(&'a self, k: &String) -> Option<&'a String> { 
     self.map.get(k) 
    } 
} 

Qual è il modo corretto di scrivere la funzione di ricerca senza copiare ° e stringa nella tabella hash?

risposta

12

Quando si prende in prestito da un RefCell, il riferimento che si ottiene ha una durata inferiore rispetto allo RefCell. Questo perché la durata del riferimento è limitata dalla protezione restituita da borrow(). Quella guardia assicura che nessun altro possa prendere un riferimento mutabile al valore finché la guardia non viene lasciata cadere.

Tuttavia, si sta tentando di restituire un valore senza tenere in vita una guardia. Se Frame ha un metodo che ha un argomento &self ma ha provato a modificare la mappa (che è possibile con RefCell — se non è necessario farlo, quindi eliminare il RefCell e scrivere &mut self sui metodi che mutano la mappa), potrebbe distruggere accidentalmente un String a cui qualcun altro ha un riferimento. Questo è esattamente il tipo di errori che il controllore del prestito è stato progettato per segnalare!

Se i valori della mappa sono effettivamente immutabili (ovvero il tuo tipo non consente di modificare i valori della mappa), puoi anche racchiuderli in un Rc nella mappa. È quindi possibile restituire un clone dello Rc<String> (questo clona solo il puntatore conteggiato di riferimento, non la stringa sottostante), che consente di rilasciare il prestito sulla mappa prima di tornare dalla funzione.

struct Frame { 
    map: RefCell<HashMap<String, Rc<String>>> 
} 

impl Frame { 
    fn lookup<'a>(&'a self, k: &String) -> Option<Rc<String>> { 
     self.map.borrow().get(k).map(|x| x.clone()) 
    } 
} 
+0

Ehi, grazie. Questa è una grande spiegazione. Sto ancora imparando, quindi ho lottato con il controllore del prestito, ma ovviamente questo ha senso. Suppongo che non capisco perché il RefCell fa la differenza - perché la versione non RefCell causa anche problemi per lo stesso motivo? –

+0

@MarcMiller: Suppongo che ormai ti rendi conto che Rust è interamente basato sulla proprietà e sul prestito. Normalmente, questi vengono controllati in fase di compilazione, tuttavia esistono costrutti basati su codice 'non sicuro 'che si trovano nel compilatore e invece verificano la correttezza in fase di esecuzione. 'RefCell' è un tale costrutto, e quindi introducendolo cambia leggermente le regole; puoi pensarlo come un mutex di lettura-scrittura per il codice a thread singolo. –

+0

@ matthieu-m: Grazie per aver fatto un salto! Potrei disturbarti a chiarire? Stai dicendo che RefCell lo cambia in una sorta di blocco del writer invece di un semplice lock del lettore, anche se non sto facendo borrow_mut? Non voglio davvero clonare i miei valori di ritorno qui dato che i miei oggetti possono essere abbastanza grandi, quindi sto cercando di fare qualcosa come ref-counting: RefCell > - ma mi fa sentire come se stessi facendo qualcosa di sbagliato. Grazie. –

Problemi correlati