One of the most intriguing concepts in Rust is ownership. Rather than undertaking garbage collection, Rust removes a value from memory once its owner is out of scope.
What does that mean? Well, let's talk about it.
Memory
Different programming languages handle memory management differently. For example, C requires you to explicitly allocate and deallocate memory. JavaScript uses a garbage collector which constantly looks for unused values and removes them from memory.
Rust takes an alternative approach. Each value in memory is tied to an owner, which is a variable. Once that variable is no longer in scope the value is removed from memory.
Rules of memory
Per the Rust Book, there are three rules of ownership.
- Each value in Rust 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.
I've already mentioned numbers one and three. But number two is a bit more complicated.
Types of memory
There are two types of memory. The first, is stack memory. It's of fixed size and stored on the stack.
The second, is heap memory. Heap memory can be any size and is referenced via a pointer to that spot in memory.
There are a few things to keep in mind with this. Stack memory is "cheaper" than heap memory. It's accessed directly rather than looked up. Stack memory is also smaller than heap memory. If it can fit in a stack slot then you don't need heap memory.
So the general takeaway is that heap memory is necessary, but using stack memory is preferable when possible.
Losing ownership
This is where ownership gets interesting. There are different rules for values stored in stack vs. heap memory.
For example, copies.
let x = 3;let y = x;
You might be familiar with "primitive" values in JavaScript. Rust has similar types of values. They're always stored in the stack. When they're copied they're duplicated. The value 3
now appears in two stack slots, one owned by x
and one owned by y
.
Now what happens when we deal with non-primitives? If you're familiar with deep vs. shallow copies that's a useful concept here.
let x = vec![3, 5, 7];let y = x;
This object is stored on the heap. Therefore, x
is storing a pointer to a spot in heap memory. If this were a shallow copy, y
would be pointing to the same spot.
This is a problem in Rust. Because that gives the same value in memory two owners. The cardinal rules require a single owner.
Additionally, Rust doesn't want to deep copy this object and end up with a second spot in heap memory. Instead, it ignores x
and confers y
as the owner of this value.
When y
goes out of scope, the object is removed from memory.
Rust is intentional
Much like the mut keyword, Rust wants you to be intentional about memory. Deep copies, or new allocations of memory, require a clone
.
let x = vec![3, 5, 7];let y = x.clone();
Otherwise, Rust will assume your last assigned variable is the owner of the value and act accordingly.
This seems complicated
Keeping track of this as your write Rust code may seem like a lot. What happens if you want to use a value without transferring ownership? That's where references come into play. I'll get to that in my next write up.