Rust neither uses garbage collection nor automatic reference counting nor manual memory management. It has chosen a rather different approach: memory is managed through a system of ownership with a set of rules that the compiler checks at compile time.
Ownership manages heap data in the following ways:
- keep track of what parts of code are using what data
- minimising duplicate data
- cleaning up unused data to avoid memory overflow
There are three basic ownership rules in Rust:
- Each value has a variable that’s called its owner
- There can only be one owner at a time
- When the owner goes out of scope, the value will be dropped
Memory and allocation
{
let s = String::from("hello"); // s is valid from this point forward
// do stuff with s
} // this scope is now over, and s is no longer valid
When working with data on the heap, we need:
- a way of requesting memory from the operating system at runtime
- a way of returning this memory when we’re done
Here, memory allocation occurs in the implementation of the String::from
method. And, Rust automatically returns the memory once a variable that owns it goes out of scope.
When a variable goes out of scope, Rust calls a special function for us. This function is called drop
, and it’s where the author of String
can put the code to return the memory. Rust calls drop
automatically at the closing curly bracket.
Move
{
let s1 = String::from("hello");
let s2 = s1; // create a shallow copy and invalidate s1
// using s1 in this scope now will generate an error
// "value used here after move"
}
s1
is a variable on the heap. When it is assigned to a new variable s2
, Rust creates a shallow copy, i.e. stack data made of references and primitives, of s1
and assign them to s2
. And it invalidates s1
so it can no longer be used.
This is done to prevent double-free errors, which occurs when two variables are pointing to the same memory. When they both go out of scope, they will try to free the same memory. It’s a memory safety bug.
Clone
If we do want to deeply copy the heap data, not just the stack data, we can use a common method called clone
.
The Copy Trait
Rust has a special annotation called the Copy
trait that we can place on stack-stored types like integers. If a type has the Copy
trait, an older variable is still usable (not invalidated) after assignment to another variable.
Generally, any group of simple scalar values can be Copy
and nothing that requires allocation or is some form of resource is Copy
. In other words, types where there is no difference between shallow and deep copying.