Ce face Rc
Rc<T> (reference counted) permite ca aceeași valoare să aibă mai mulți proprietari. Rc::new(x) o pune în heap cu un contor egal cu 1. Rc::clone(&r) nu copiază valoarea: creează încă un pointer spre ea și mărește contorul. Când un Rc dispare, contorul scade; la 0, valoarea este eliberată.
Rc::strong_count(&r) spune câți proprietari are valoarea acum. Prin convenție se scrie Rc::clone(&r), nu r.clone(), ca să fie clar că este o operație ieftină.
Valoarea dintr-un Rc poate fi doar citită. Pentru modificare, combină-l cu RefCell (Rc<RefCell<T>>). Rc nu poate trece între fire; acolo folosește Arc.
Parametri
| Parametru | Descriere |
|---|---|
value |
Valoarea împărțită (la new). |
Ce returnează
new și clone returnează un Rc<T>; strong_count returnează numărul de proprietari.
Exemple
Exemplul 1: Contorul crește și scade
Două familii folosesc aceeași adresă; în blocul interior apare un al treilea proprietar, care dispare la final de bloc.
use std::rc::Rc;
fn main() {
let adresa = Rc::new(String::from("Str. Lăpușneanu 10, Iași"));
println!("proprietari: {}", Rc::strong_count(&adresa));
let familia_pop = Rc::clone(&adresa);
println!("proprietari: {}", Rc::strong_count(&adresa));
{
let familia_ionescu = Rc::clone(&adresa);
println!("în bloc: {} (Ionescu: {familia_ionescu})", Rc::strong_count(&adresa));
}
println!("după bloc: {}", Rc::strong_count(&adresa));
println!("Pop: {familia_pop}");
}
Rezultat
proprietari: 1
proprietari: 2
în bloc: 3 (Ionescu: Str. Lăpușneanu 10, Iași)
după bloc: 2
Pop: Str. Lăpușneanu 10, Iași
Exemplul 2: Aceeași valoare în două liste
Produsul nu este copiat: ambele liste țin un Rc spre același obiect. Rc::ptr_eq confirmă că e aceeași adresă.
use std::rc::Rc;
#[derive(Debug)]
struct Produs {
nume: String,
pret: f64,
}
fn main() {
let cafea = Rc::new(Produs { nume: String::from("cafea"), pret: 24.5 });
let cos_ana = vec![Rc::clone(&cafea)];
let cos_dan = vec![Rc::clone(&cafea), Rc::new(Produs { nume: String::from("ceai"), pret: 12.0 })];
println!("Ana: {:?}", cos_ana);
println!("Dan are {} produse, total {} lei", cos_dan.len(), cos_dan.iter().map(|p| p.pret).sum::<f64>());
println!("același obiect: {}", Rc::ptr_eq(&cos_ana[0], &cos_dan[0]));
println!("proprietari cafea: {}", Rc::strong_count(&cafea));
println!("{}", cafea.nume);
}
Rezultat
Ana: [Produs { nume: "cafea", pret: 24.5 }]
Dan are 2 produse, total 36.5 lei
același obiect: true
proprietari cafea: 3
cafea
Greșeli frecvente
- Încerci să modifici valoarea:
Rc::new(vec![]).push(1)nu compilează (cannot borrow as mutable). FoloseșteRc<RefCell<Vec<_>>>. - Creezi cicluri de
Rc(A ține B și B ține A): contorul nu ajunge niciodată la 0 și memoria nu se eliberează. Pentru legăturile înapoi foloseșteWeak. - Trimiți un
Rcîntr-un fir: eroareaRc<...> cannot be sent between threads safely. FoloseșteArc.
Comenzi înrudite
Weak: Referință slabă spre o valoare din Rc: nu o ține în viață; upgrade() dă Some(Rc) dacă valoarea mai există.Arc: Varianta lui Rc care poate fi împărțită între fire: contorul de proprietari este actualizat atomic.RefCell: Permite modificarea unei valori prin referință comună (&), verificând regulile de împrumut la rulare, nu la compilare.Box: Pune o valoare în memoria heap și o deține; folosit pentru tipuri recursive și pentru Box<dyn Trait>.