Box. 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.
Box new. 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.
Input and output. 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.
Version 1 This code creates a Vector of Box types. It calls "Box::new" to create each element, and pushes the Boxes to the vector.
Version 2 This code use stack allocation, which is the default. It directly allocates the 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 }]
Box array. 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.
However We can create an array of Boxes (or boxed slices) by creating a vector of Boxes, and then converting the vector into an array.
Step 1 We create the vector of Boxes. For our example, we just push a Box of the numbers 0 through 9 inclusive.
Step 2 We convert the vector into an array. We must use 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
Memory size optimization. With Box, we move memory to a separate location on the heap. This can improve performance when a struct is used in hot code.
Here We move the 1000-element array allocation to a separate place in the heap.
And This reduces memory usage of the 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
A summary. 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.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Nov 15, 2023 (edit).