Skip to content

Rust ownership

Rust ownership

Rust (like C) has a concept of “string literals”, which are text data types with a length known at compile time. Because the length is known at compile time, these string literals (str in Rust) can be stored on the stack, which makes them faster since they’re easily indexed, and also allows them to be easily copied. Therefore, Rust allows automatic copying of stack data types.

However, Rust also has a concept of string references, which are mutable strings that do not necessarily have a known size at compile time. When a String (the string reference data structure in Rust) is created in the Rust runtime, the runtime must request heap space from the computer in order to store the data. The data for a String value looks like:

name value
len 5
capacity 5
                      \     | index | value |
                       \    | ---   | ---   |
                        \_> | 0     | h     |
                            | 1     | e     |
                            | 2     | l     |
                            | 3     | l     |
                            | 4     | o     |

This String value contains the word “hello”. 5 bytes of memory in length, 5 bytes received from the OS for capacity, and a pointer to its address in the heap.

Therefore, the value returned by String is actually a reference to the data in the heap, which means that heap data (unknown size at compile time) is passed by reference in Rust.This also means that heap data references cannot be owned by multiple entities at one time, since one entity may go out of scope and cause the reference to the data on the heap to be “dropped” from memory, potentially leaving another reference still “alive” even though the data has been dropped. So, when that second reference goes out of scope, a “double free” error will occur because the memory has already been freed. Rust will not allow multiple references to the same data to exist in the same scope.

When a reference to heap data is assigned to a new variable or passed to a new scope, it’s known as a move. It’s called a move because the previous reference is now invalidated, so the reference has simply moved from one place to another in terms of ownership. This means that any “deep” copying of data must be explicit, because it will rarely automatically copy data. This also saves performance.

NOTE: if one does want to deeply copy heap data, use the clone method (if the data type allows it).

NOTE: The Copy trait in Rust is an annotation that can be placed on stack-bound data types. Copy cannot coexist with the Drop trait, however.