Box<T>
Box
— це вказівник на дані в купі:
fn main() { let five = Box::new(5); println!("five: {}", *five); }
Box<T>
реалізує Deref<Target = T>
, що означає, що ви можете викликати методи з T
безпосередньо на Box<T>
.
Рекурсивні типи даних або типи даних з динамічним розміром не можуть зберігатися вбудованими без перенаправлення вказівника, що можна обійти за допомогою Box
:
#[derive(Debug)] enum List<T> { /// Непорожній список: перший елемент та решта списку. Element(T, Box<List<T>>), /// Порожній список. Nil, } fn main() { let list: List<i32> = List::Element(1, Box::new(List::Element(2, Box::new(List::Nil)))); println!("{list:?}"); }
-
Box
схожий наstd::unique_ptr
у C++, за винятком того, що він гарантовано не буде null. -
Box
може бути корисним, коли ви:- маєте тип, розмір якого не може бути відомий під час компіляції, але компілятор Rust хоче знати точний розмір.
- хочете передати володіння на великий обсяг даних. Щоб уникнути копіювання великих обсягів даних у стеку, натомість зберігайте дані в купі в
Box
, щоб переміщувався лише вказівник.
-
Якщо би
Box
не використовувався, і ми намагалися вставитиList
безпосередньо вList
, компілятор не зміг би обчислити фіксований розмір структури в пам’яті (List
мав би нескінченний розмір). -
Box
вирішує цю проблему, оскільки має той самий розмір, що й звичайний вказівник, і лише вказує на наступний елементList
у купі. -
Видаліть
Box
у визначенні списку та відобразіть помилку компілятора. Ми отримаємо повідомлення "recursive without indirection", тому що для рекурсії даних ми повинні використовувати посередництво,Box
або якесь посилання, замість того, щоб зберігати значення безпосередньо.
Більше інформації для вивчення
Нішева оптимізація
Хоча Box
виглядає як std::unique_ptr
у C++, він не може бути порожнім/нульовим. Це робить Box
одним з типів, які дозволяють компілятору оптимізувати зберігання деяких переліків.
Наприклад, Option<Box<T>>
має такий самий розмір, як і просто Box<T>
, оскільки компілятор використовує NULL-значення для розрізнення варіантів замість використання явного тегу ("Оптимізація нульового вказівника"):
use std::mem::size_of_val; struct Item(String); fn main() { let just_box: Box<Item> = Box::new(Item("Just box".into())); let optional_box: Option<Box<Item>> = Some(Box::new(Item("Optional box".into()))); let none: Option<Box<Item>> = None; assert_eq!(size_of_val(&just_box), size_of_val(&optional_box)); assert_eq!(size_of_val(&just_box), size_of_val(&none)); println!("Розмір just_box: {}", size_of_val(&just_box)); println!("Розмір optional_box: {}", size_of_val(&optional_box)); println!("Розмір none: {}", size_of_val(&none)); }