Understanding Rust’s Ownership, Borrowing, and Memory Management

Ajmal
CoinsBench
Published in
3 min readMay 4, 2024

--

Rust’s memory safety guarantees without a garbage collector come from its unique approach to ownership, borrowing, and the memory model (stack vs. heap). Understanding these concepts deeply can unlock Rust’s full potential while also avoiding common pitfalls that new and even experienced developers face.

The Basics of Ownership

Ownership is a set of rules that governs how a Rust program manages memory. It is enforced at compile time, preventing bugs that are usually associated with memory management such as double frees, leaks, and data races.

The Rules of Ownership

  1. Each value in Rust has a variable that’s called its owner.
  2. There can only be one owner at a time.
  3. When the owner goes out of scope, the value will be dropped.

Here’s a simple example:

fn main() {
let s = String::from("hello"); // s owns the string
} // Here, s goes out of scope and 'hello' is dropped automatically

Borrowing in Rust

Borrowing is another integral concept in Rust, allowing multiple parts of your code to access data without taking ownership. It does so via references.

Rules for Borrowing

  1. References must always be valid.
  2. At any given time, you can have either one mutable reference or any number of immutable references.
  3. References must not outlive the data they refer to.

Here’s an example of borrowing in action:

fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}

s is a reference to s1, allowing calculate_length to use s1 without taking ownership.

Lifetime in Rust

Lifetimes ensure that all borrows are valid for the duration they are in use, preventing dangling pointers and other common bugs.

Example demonstrating Lifetimes:

fn main() {
let r; // ---------+-- r
// |
{ // |
let x = 5; // -+-- x |
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // |
} // ---------+

This example will not compile because r outlives x, showing how Rust's compiler prevents errors related to dangling references.

Stack vs. Heap Memory Management

Understanding where Rust stores data (stack or heap) is crucial for performance.

  • Stack: Used for static data whose size is known at compile time, allowing fast access.
  • Heap: Used for dynamic data where the size can change, which is slower but essential for complex structures.

Ownership Behavior on Stack vs. Heap

Stack data is cheap to create and manage. When a variable on the stack is used to initialise another, Rust by default makes a copy of the data:

fn main() {
let x = 5;
let y = x; // Copy of x is created and assigned to y
}

For heap data, ownership behavior is more complex due to the cost of managing heap memory. When ownership is transferred, the original variable no longer has access to the data:

fn main() {
let heap_i8 = Box::new(5);
let heap_i8_two = heap_i8; // Ownership of the data on the heap is moved to heap_i8_two
// Error: heap_i8 can no longer be used here
}

To maintain two active variables that can use the same heap data, cloning is necessary, which creates a deep copy of the data:

fn main() {
let heap_i8 = Box::new(5);
let heap_i8_two = heap_i8.clone(); // A copy of the heap data is made for heap_i8_two
// Both heap_i8 and heap_i8_two can now be used independently
}

Conclusion

The combination of ownership, borrowing, and Rust’s memory model (stack vs. heap) provides a robust framework for handling memory efficiently and safely. Understanding these principles allows developers to avoid common pitfalls associated with manual memory management and leverage Rust’s performance and safety features effectively. Whether building system-level software or complex applications with dynamic memory needs, mastering Rust’s memory management techniques ensures reliable and high-performance solutions.

--

--

Ph.D. in telecom, a Researcher and Engineer with architecture and protocol design experience in NR and Web3 space..