Box

The most straightforward of the standard smart pointers is the Box. A Box does what we've been discussing so far: it stores a data value on the heap, while ensuring that it still follows the lifetime rules as if it were actually part of the Box value itself.

Here's an example. First, we'll create a data type for the data we want to store on the heap:

pub struct Person {
pub name: String,
pub validated: bool,
}

Now, creating and using the Box itself is easy:

let jack = Box::new(Person { name: "Jack".to_string(), validated: true });
let x = &jack.name;
println!("The person in the box is {}", x);

The first line creates a Box. We have to give it a data value to store, because one thing Rust is not okay with is an empty Box, so we initialize a Person object and pass it to the function, which creates a new Box to be used as its internal value.

Why does Rust not allow an empty Box, or any other kind of smart pointer, for that matter? Because if it did, then it would have to worry about whether a given smart pointer referred to initialized memory or not whenever that smart pointer's contents were accessed. Requiring that as long as the smart pointer exists the memory it manages must contain a valid data value simplifies many things and makes a common kind of error impossible.

Once we have an initialized the Box, we can mostly treat it as if it was a normal borrow of the contained data. We can move the data back out onto the stack by dereferencing it:

let x = *jack;

This moves the Person value from inside the Box named jack to the x variable, and renders jack unusable.

We can also access the contained data value's data and functions through the Box, again as if it were a borrow of the contained data:

let x = &jack.name;
println!("The person in the box is {}", x);

Here, we're asking to borrow jack.name into x, then printing out that name. We could have also gotten the same result by doing the following:

println!("The person in the box is {}", jack.name);

But that actually works in a very different way. The first example borrows the name, and then prints out that borrowed String value. The second one actually calls a function called jack.name.fmt, which has an immutable borrow as its self parameter. This works out because Rust is smart about dereferencing and function calls.

Where did fmt get called? The answer is that println! is a macro, which means that it's not actually a function, but instead is kind of like pasting some code right into the program here. The pasted code calls fmt, so it's as if we called fmt ourselves. In Rust, we can recognize macros because their names always end with !, and function names never do.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.200.71