In Rust, memory management is done with ownership, which follows three core rules:

  • Each value has an owner.
  • There can only be one owner at a time.
  • The value will be dropped (and memory freed, if necessary) when the owner goes out of scope.

Variables with a known size (scalar types) are put on the stack. Stack variable types that implement shallow copies (the Copy trait) are: all integer types, the Boolean type, all floating-point types, char, and tuples that contain Copy types.

Variables with an unfixed size are put on the heap. When we reassign variables on the heap, we have a move. The data itself isn’t copied, but the pointer, length, and capacity of the data is copied. The first variable is also invalidated.

Rust will never automatically create deep copies of data. Saves on runtime!

let s1 = String::from("hello");
let s2 = s1; // s1 no longer valid

Methods like .clone() do allow us to perform a deep copy if that’s what we intend.

Functions

Passing a value to functions perform similarly to an assignment, and will do a shallow copy or move depending on the type of the variable. When we pass a heap variable to a function, we transfer ownership scope to that function. If it’s not returned, the variable goes out of scope after the function end.

We can also use references (like in C++) to avoid transferring ownership. One notable difference is that we also need to create a reference in the function call (&name) in addition to the function signature to support references. References fit in the ownership model by doing “borrowing”: the new scope doesn’t own the memory and it returns it when done.