Ce face Box
Box::new(x) mută valoarea x în heap (memoria alocată dinamic) și îți dă un Box<T>, un pointer care o deține. Când box-ul iese din domeniu, valoarea este eliberată automat. Folosești box-ul ca pe valoarea însăși: *b o citește, iar metodele lui T se apelează direct (b.len()).
Ai nevoie de Box în trei situații: tipuri recursive (o listă sau un arbore care se conține pe el însuși, a cărui dimensiune altfel nu ar putea fi calculată), obiecte de tipuri diferite care implementează același trait (Vec<Box<dyn Forma>>) și valori mari pe care vrei să le muți ieftin (se copiază doar pointerul).
Un Box are un singur proprietar. Pentru o valoare deținută de mai mulți, folosește Rc sau Arc.
Parametri
| Parametru | Descriere |
|---|---|
x |
Valoarea mutată în heap. |
Ce returnează
Un Box<T> care deține valoarea.
Exemple
Exemplul 1: Un tip recursiv: lista de sarcini
Fără Box, enum-ul Lista s-ar conține pe el însuși direct și ar avea dimensiune infinită (eroarea recursive type has infinite size).
enum Lista {
Element(&'static str, Box<Lista>),
Gol,
}
fn afiseaza(lista: &Lista) {
match lista {
Lista::Element(sarcina, urmatorul) => {
println!("- {sarcina}");
afiseaza(urmatorul);
}
Lista::Gol => println!("(sfârșit)"),
}
}
fn main() {
let lista = Lista::Element(
"cumpără pâine",
Box::new(Lista::Element("sună-o pe Ana", Box::new(Lista::Gol))),
);
afiseaza(&lista);
}
Rezultat
- cumpără pâine
- sună-o pe Ana
(sfârșit)
Exemplul 2: Box: obiecte de tipuri diferite într-un vector
Cercul și dreptunghiul au tipuri diferite, dar ambele implementează Forma; în box le putem ține în același vector.
trait Forma {
fn aria(&self) -> f64;
fn nume(&self) -> String;
}
struct Cerc {
raza: f64,
}
struct Dreptunghi {
l: f64,
h: f64,
}
impl Forma for Cerc {
fn aria(&self) -> f64 {
std::f64::consts::PI * self.raza * self.raza
}
fn nume(&self) -> String {
format!("cerc (r = {})", self.raza)
}
}
impl Forma for Dreptunghi {
fn aria(&self) -> f64 {
self.l * self.h
}
fn nume(&self) -> String {
format!("dreptunghi {} x {}", self.l, self.h)
}
}
fn main() {
let forme: Vec<Box<dyn Forma>> = vec![
Box::new(Cerc { raza: 1.5 }),
Box::new(Dreptunghi { l: 4.0, h: 2.5 }),
];
for f in &forme {
println!("{}: aria = {:.2}", f.nume(), f.aria());
}
}
Rezultat
cerc (r = 1.5): aria = 7.07
dreptunghi 4 x 2.5: aria = 10.00
Exemplul 3: Folosirea valorii din box
fn main() {
let numar = Box::new(41);
let urmator = *numar + 1;
println!("{numar} -> {urmator}");
let mut oras = Box::new(String::from("Iași"));
oras.push_str(", România");
println!("{oras} are {} octeți", oras.len());
}
Rezultat
41 -> 42
Iași, România are 15 octeți
Greșeli frecvente
- Pui în
Boxvalori mici fără motiv (Box<i32>): adaugi o alocare și o indirectare inutile. - Încerci să copiezi un box cu
let b2 = b;și apoi foloseștib: box-ul a fost mutat. Foloseșteb.clone()dacăT: ClonesauRcpentru proprietate comună.
Comenzi înrudite
Box::leak: Renunță la un Box fără să elibereze memoria și dă o referință valabilă până la finalul programului (&’static).Rc: Pointer cu numărarea referințelor: mai mulți proprietari împart aceeași valoare, eliberată când dispare ultimul.Arc: Varianta lui Rc care poate fi împărțită între fire: contorul de proprietari este actualizat atomic.mem::size_of: Returnează câți octeți ocupă în memorie o valoare de tipul T (fără ce alocă eventual în heap).