2015-04-01 9 views
8

Ho una struttura con mutabilità interna.Come posso restituire un riferimento a qualcosa all'interno di un RefCell senza interrompere l'incapsulamento?

use std::cell::RefCell; 

struct MutableInterior { 
    hideMe: i32, 
    vec: Vec<i32>, 
} 
struct Foo { 
    //although not used in this particular snippet, 
    //the motivating problem uses interior mutability 
    //via RefCell. 
    interior: RefCell<MutableInterior>, 
} 

impl Foo { 
    pub fn getItems(&self) -> &Vec<i32> { 
     &self.interior.borrow().vec 
    } 
} 

fn main() { 
    let f = Foo { 
     interior: RefCell::new(MutableInterior { 
      vec: Vec::new(), 
      hideMe: 2, 
     }), 
    }; 
    let borrowed_f = &f; 
    let items = borrowed_f.getItems(); 
} 

genera l'errore:

error: borrowed value does not live long enough 
    --> src/main.rs:16:10 
    | 
16 |   &self.interior.borrow().vec 
    |   ^^^^^^^^^^^^^^^^^^^^^^ does not live long enough 
17 |  } 
    |  - temporary value only lives until here 
    | 
note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:40... 
    --> src/main.rs:15:41 
    | 
15 |  pub fn getItems(&self) -> &Vec<i32> { 
    | _________________________________________^ starting here... 
16 | |   &self.interior.borrow().vec 
17 | |  } 
    | |_____^ ...ending here 

Il problema è che non posso avere una funzione su Foo che restituisce un prestito vec, perché il prestito vec è valido solo per la durata del Ref , ma lo Ref esce immediatamente dall'ambito.

penso che il Ref deve restare because:

RefCell uses Rust's lifetimes to implement 'dynamic borrowing', a process whereby one can claim temporary, exclusive, mutable access to the inner value. Borrows for RefCells are tracked 'at runtime', unlike Rust's native reference types which are entirely tracked statically, at compile time. Because RefCell borrows are dynamic it is possible to attempt to borrow a value that is already mutably borrowed; when this happens it results in task panic.

Ora mi potrebbe invece scrivere una funzione come questo che restituisce l'intero interno:

pub fn getMutableInterior(&self) -> std::cell::Ref<MutableInterior>; 

Tuttavia, questo espone potenzialmente campi (MutableInterior.hideMe in questo esempio) che sono dettagli di implementazione davvero privati ​​a Foo.

Idealmente, voglio solo esporre lo stesso vec, potenzialmente con una guardia per implementare il comportamento di prestito dinamico. Quindi i chiamanti non devono scoprire hideMe.

risposta

14

È possibile creare una nuova struct simile al Ref<'a,T> guardia restituito da RefCell::borrow(), al fine di avvolgere questo Ref ed evitare di dover andando fuori portata, in questo modo:

use std::cell::Ref; 

struct FooGuard<'a> { 
    guard: Ref<'a, MutableInterior> 
} 

allora, si può attuare il Deref tratto di essa, in modo che possa essere utilizzato come se fosse un :

use std::ops::Deref; 

impl<'b> Deref for FooGuard<'b> { 
    type Target = Vec<i32>; 

    fn deref(&self) -> &Vec<i32> { 
     &self.guard.vec 
    } 
} 

dopo che, sim ply aggiornare il metodo di getItems() per restituire un'istanza FooGuard:

impl Foo { 
    pub fn getItems(&self) -> FooGuard { 
     FooGuard { guard: self.interior.borrow() } 
    } 
} 

e Deref fa la magia:

fn main() { 
    let f = Foo { 
     interior: RefCell::new(MutableInterior{ vec: Vec::new(), hideMe: 2 }) 
    }; 
    let borrowed_f = &f; 
    let items = borrowed_f.getItems(); 
    let v: &Vec<i32> = &items; 
} 
+0

E 'questo l'unico/modo idiomatico per fare questo? Sembra un po 'di problemi ... Anche se suppongo invece di un metodo getItems(), è possibile prendere in prestito gli interni in un blocco direttamente dove andrebbe fuori ambito (o qualcosa ...) – norcalli

+2

@Norcalli Nello specifico caso di 'RefCell', l'oggetto deve essere notificato quando il riferimento esce dall'ambito (è ciò che fa il distruttore di' Ref'). Qui, abbiamo bisogno di mantenere questo comportamento (l'errore dell'OP era dovuto al fatto che l'istanza di 'Ref' veniva rilasciata troppo presto), e quindi la incapsulava. – Levans

Problemi correlati