Ce face RefCell
Regula de bază din Rust: ori mai mulți cititori (&T), ori un singur modificator (&mut T). De obicei compilatorul verifică regula. RefCell<T> mută verificarea la rulare: poți modifica valoarea chiar dacă ai doar &RefCell. Acest lucru se numește mutabilitate interioară.
borrow() dă un împrumut de citire (Ref), borrow_mut() unul de scriere (RefMut). Împrumutul ține până când variabila Ref/RefMut dispare. Dacă încalci regula (de exemplu ceri borrow_mut cât timp există alt împrumut), programul intră în panică. Variantele try_borrow și try_borrow_mut returnează Result în loc de panică.
Cel mai des apare ca Rc<RefCell<T>>: mai mulți proprietari care pot modifica aceeași valoare. Nu poate fi folosit între fire; acolo există Mutex și RwLock.
Parametri
| Parametru | Descriere |
|---|---|
value |
Valoarea inițială (la new). |
Ce returnează
borrow: Ref<T> (se folosește ca &T). borrow_mut: RefMut<T> (se folosește ca &mut T).
Exemple
Exemplul 1: Coș de cumpărături comun
Ana și Dan țin același coș prin Rc; fiecare adaugă produse prin borrow_mut, deși au doar referințe comune.
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let cos = Rc::new(RefCell::new(Vec::new()));
let cos_ana = Rc::clone(&cos);
let cos_dan = Rc::clone(&cos);
cos_ana.borrow_mut().push("lapte");
cos_dan.borrow_mut().push("pâine");
cos_ana.borrow_mut().push("ouă");
println!("coș: {:?}", cos.borrow());
println!("produse: {}", cos.borrow().len());
}
Rezultat
coș: ["lapte", "pâine", "ouă"]
produse: 3
Exemplul 2: Modificare prin &self
Metoda cauta primește &self, dar ține un jurnal al căutărilor în RefCell.
use std::cell::RefCell;
struct Catalog {
elevi: Vec<(&'static str, u8)>,
jurnal: RefCell<Vec<String>>,
}
impl Catalog {
fn cauta(&self, nume: &str) -> Option<u8> {
self.jurnal.borrow_mut().push(format!("căutare: {nume}"));
self.elevi.iter().find(|(n, _)| *n == nume).map(|(_, nota)| *nota)
}
}
fn main() {
let catalog = Catalog { elevi: vec![("Ana", 10), ("Mihai", 8)], jurnal: RefCell::new(Vec::new()) };
println!("{:?}", catalog.cauta("Mihai"));
println!("{:?}", catalog.cauta("Radu"));
for linie in catalog.jurnal.borrow().iter() {
println!("{linie}");
}
}
Rezultat
Some(8)
None
căutare: Mihai
căutare: Radu
Exemplul 3: Două împrumuturi în același timp
try_borrow_mut arată conflictul fără panică. Cu borrow_mut, aceeași situație ar opri programul cu RefCell already borrowed.
use std::cell::RefCell;
fn main() {
let note = RefCell::new(vec![9, 7]);
let citire = note.borrow();
println!("citesc: {:?}", *citire);
println!("scriere în timpul citirii: {:?}", note.try_borrow_mut().map(|_| ()));
drop(citire);
note.borrow_mut().push(10);
println!("după: {:?}", note.borrow());
}
Rezultat
citesc: [9, 7]
scriere în timpul citirii: Err(BorrowMutError)
după: [9, 7, 10]
Greșeli frecvente
- Ții un
borrow()într-o variabilă și apoi apeleziborrow_mut()în aceeași funcție: panică la rulare. Limitează împrumutul la un bloc sau eliberează-l cudrop. - Folosești
RefCellpeste tot ca să „scapi” de compilator. Verificările se mută la rulare și erorile devin panici; folosește-l doar unde chiar ai nevoie. - Pentru valori
Copysimple (numere,bool),Celleste mai simplu și nu poate intra în panică.
Comenzi înrudite
RefCell::borrow: borrow() împrumută valoarea din RefCell pentru citire, borrow_mut() pentru scriere; conflictele dau panică.Cell: Celulă cu mutabilitate interioară pentru valori Copy: get() copiază valoarea afară, set() pune una nouă, prin &self.Rc: Pointer cu numărarea referințelor: mai mulți proprietari împart aceeași valoare, eliberată când dispare ultimul.Mutex: Protejează o valoare folosită din mai multe fire: lock() așteaptă până când firul curent este singurul cu acces.