Rust Memory Management: Ownership, References, and Borrowing

Ownership

Rust manages memory through a system of ownership with scope-based deallocation. This ensures memory safety without requiring a garbage collector. The core principles are:

  • Every value has a specific variable assigned as its owner.
  • There can only be one owner at any given moment.
  • The value is dropped (deallocated) when the owner variable goes out of scope.

Data Interaction: Moving and Cloning

Move Semantics

When assigning a String (or any heap-allocated data) to another variable, the ownership is transferred. The original variable becomes invalid.

let original = String::from("data");
let transferred = original;
// `original` is no longer valid here and cannot be used

Cloning

If you need to retain the original variable, you must explicitly use the .clone() method to perform a deep copy of the heap data.

let original = String::from("data");
let duplicate = original.clone();
// Both `original` and `duplicate` are valid

Copy Trait

Types stored entirely on the stack, such as integers, booleans, floating-point numbers, and tuples (containing only Copy types), implement the Copy trait. These are copied automatically rather than moved.

Ownership and Functions

Passing a value to a function behaves similarly to assignment. If the type does not implement the Copy trait, ownership is moved to the function parameter. Returning values can transfer ownership back to the caller.

let mut resource = String::from("resource");
resource = take_ownership(resource); // Ownership moved to function and returned

fn take_ownership(data: String) -> String {
    data // Ownership is returned to the caller
}

References and Borrowing

The & symbol denotes a reference, which allows you to access data without taking ownership. This action is referrred to as borrowing. The * symbol is used for dereferencing.

Immutable References

You can pass a reference to a function to read data without modifying it. The referenced value must remain valid for as long as the reference is active.

let message = String::from("hello world");
let length = compute_length(&message); // Borrows `message` without taking ownership

fn compute_length(s: &String) -> usize {
    s.len()
} // `s` goes out of scope, but since it does not have ownership, nothing is dropped

Mutable References

To modify borrowed data, you must use a mutable reference. Both the variable and the reference must be declared with the mut keyword.

let mut editable = String::from("base");
modify(&mut editable);

fn modify(s: &mut String) {
    s.push_str(" text");
}

Borrowing Rules and Data Races

Rust enforces strict borrowing rules at compile time to prevent data races. A data race occurs when:

  • Two or more pointers access the same data simultaneously.
  • At least one pointer is being used to write to the data.
  • There is no mechanism to synchronize the access.

The specific restriction regarding references is:

You may have either one mutable reference OR any number of immutable references, but never both simultaneously. References must always be valid.

Scope Examples

The following examples illustrate how the compiler enforces these rules based on variable scope.

// Scenario A: Multiple mutable references simultaneously
let mut data = String::from("example");
let r1 = &mut data;
let r2 = &mut data;
// Error: Cannot borrow `data` as mutable more than once at a time
println!("{}, {}", r1, r2); 

// Scenario B: Mixing mutable and immutable references
let mut data = String::from("example");
let r1 = &data; // No problem
let r2 = &data; // No problem
let r3 = &mut data; // Error: Cannot borrow `data` as mutable because it is also borrowed as immutable
println!("{}, {}, and {}", r1, r2, r3);

// Scenario C: Valid usage (Non-overlapping scopes)
let mut data = String::from("example");
let r1 = &data; // No problem
let r2 = &data; // No problem
println!("{} and {}", r1, r2); 
// The scope of r1 and r2 ends here after their last usage
let r3 = &mut data; // No problem, as r1 and r2 are no longer used
println!("{}", r3);

Tags: rust ownership Borrowing memory-safety References

Posted on Sun, 18 Oct 2026 16:49:30 +0000 by alfpalafox