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