The Box structure can be used to store objects as traits on the
heap. This allows to use polymorphism on objects that can be owned
by other objects, instead of borrowing them.
Let us create a Shape trait for which we declare an area() method:
trait Shape {
fn area(&self) -> f64;
}We define two structs Circle and Rectangle:
struct Circle {
radius: f64,
}
struct Rectangle {
width: f64,
height: f64,
}And implement the Shape trait for both structures:
impl Shape for Circle {
fn area(&self) -> f64 {
self.radius * self.radius * std::f64::consts::PI
}
}
impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}The two instances we create now can be seen as shapes using their
common Shape trait:
let circ = Circle { radius: 2.0 };
let rect = Rectangle { width: 3.0, height: 4.0 };One way to show this, is to build a vector of Shape references that
mixes a Rectangle and a Circle objects, and then to loop on the
vector’s items and use the Shape’s method on them:
{
let shapes: Vec<&dyn Shape> = vec![&circ, &rect];
for shape in &shapes {
println!("Area: {}", shape.area());
}
}Area: 12.566370614359172
Area: 12
()However we are limited by the fact that these are only references, meaning their lifetime depends on the lifetime of the original objects that reside on the stack. What if we want to move these objects altogether to another data structure on the heap? Using the Box structure we can move objects that have a common trait.
Here is again a vector of Shape objects, but this time we use a
Box to store them:
let shapes: Vec<Box<dyn Shape>> = vec![
Box::new(circ),
Box::new(rect),
];We can access them the same way:
for shape in &shapes {
println!("Area: {}", shape.area());
}Area: 12.566370614359172
Area: 12
()