Rust: Ownership

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:

  1. a way of requesting memory from the operating system at runtime
  2. 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.