To use a struct
instance, we must first allocate it. By default, allocation is done on the stack region of memory—this is faster unless too many objects are created.
We can wrap a struct
in the Box type. The Box will manage the memory of its enclosing type on its own—it will handle allocation, and delete the memory as well.
Suppose we want to create a Vector
of structs. The struct
name is BirdInfo
, and it contains information about birds. We can use Box for the vector.
HEAP: Box<BirdInfo> Box<BirdInfo> STACK: BirdInfo BirdInfo
We have 2 versions of similar code. We add several BirdInfo
structs to each vector, and print their contents at the end of main()
.
Vector
of Box types. It calls "Box::new
" to create each element, and pushes the Boxes to the vector.BirdInfo
structs, without Box.#[derive(Debug)] struct BirdInfo<'a> { color: &'a str, size: i32 } fn main() { // Version 1: allocate structs with Box on heap. let mut birds: Vec<Box<BirdInfo>> = Vec::new(); for i in 0..2 { birds.push(Box::new(BirdInfo { color: "blue", size: 4 })); } // Version 2: allocate structs on stack. let mut birds2: Vec<BirdInfo> = Vec::new(); for i in 0..2 { birds2.push(BirdInfo { color: "red", size: 5 }); } // Print results. println!("HEAP: {:?}", birds); println!("STACK: {:?}", birds2); }HEAP: [BirdInfo { color: "blue", size: 4 }, BirdInfo { color: "blue", size: 4 }] STACK: [BirdInfo { color: "red", size: 5 }, BirdInfo { color: "red", size: 5 }]
How can we create an array of Boxes? It is not possible to initialize the array with a constant Box, as Box does not implement the Copy trait.
try_into()
as the conversion can fail if the vector size is not correct.fn main() { // Step 1: Create a vector of boxes. let mut values = vec![]; for i in 0..10 { values.push(Box::new(i)); } // Step 2: Convert vector into array. let array: [Box<i32>; 10] = values.try_into().unwrap(); println!("{}", array.len()); }10
With Box, we move memory to a separate location on the heap. This can improve performance when a struct
is used in hot code.
struct
from 1000 to 8 bytes. For some access patterns this can speed up code.struct Example { array: [u8; 1000] } struct ExampleWithBox { array: Box<[u8; 1000]> } fn main() { println!("size of Example: {}", std::mem::size_of::<Example>()); println!("size of ExampleWithBox: {}", std::mem::size_of::<ExampleWithBox>()); }size of Example: 1000 size of ExampleWithBox: 8
By using Box, we change how structs are allocated—Box places elements on the heap instead of the stack. This changes performance, and is more useful for larger, long-lived data.