Skip links

Table of Contents

Rust Ownership and Borrowing [Simplified]

TL;DR

  • Rust uses an ownership model to manage memory safely without needing a garbage collector.
  • Each value has one owner — when that owner goes out of scope, the value is automatically dropped.
  • Ownership can be transferred when assigning variables or passing to functions.
  • Borrowing lets you use data without taking ownership, using references (&T or &mut T).
  • Rust enforces strict rules to prevent data races and dangling references, all checked at compile time.

Rust is a systems programming language that guarantees memory safety without requiring a garbage collector. At the heart of this guarantee are two key concepts: rust ownership and borrowing. These features make Rust unique among programming languages, enabling it to prevent common bugs like null pointer dereferences, dangling pointers, and data races at compile time.

What is Rust Ownership?

Ownership is Rust’s most distinctive feature. It’s a set of rules that govern how memory is managed in Rust programs. The ownership system ensures that memory is automatically freed when a variable goes out of scope, while also preventing common memory-related bugs.

The Three Rules of Ownership

  1. Each value in Rust has an owner – There’s always exactly one variable that owns a value.
  2. There can only be one owner at a time – When you assign a value to another variable or pass it to a function, ownership is transferred.
  3. When the owner goes out of scope, the value is dropped – Rust automatically calls the drop function to clean up the memory.
fn main() {
    let s1 = String::from("hello");  // s1 owns the String
    let s2 = s1;                     // Ownership moves to s2

    // println!("{}", s1);           // This would error - s1 no longer owns the String
    println!("{}", s2);              // This works fine
} // s2 goes out of scope and the String is dropped

What is Rust Borrowing?

While ownership ensures memory safety, transferring ownership every time you want to use a value would be cumbersome. This is where borrowing comes in – it allows you to temporarily access a value without taking ownership of it.

The Two Types of Borrowing

  1. Immutable borrows (&T) – Allow reading but not modifying the borrowed value.
  2. Mutable borrows (&mut T) – Allow modifying the borrowed value.
fn main() {
    let s = String::from("hello");

    // Immutable borrow
    let len = calculate_length(&s);
    println!("The length of '{}' is {}.", s, len);

    // Mutable borrow
    let mut s = String::from("hello");
    change_string(&mut s);
    println!("{}", s);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

fn change_string(s: &mut String) {
    s.push_str(", world!");
}

The Borrowing Rules

Rust enforces strict rules around borrowing to prevent data races and other memory issues:

  1. You can have either one mutable reference or any number of immutable references to a particular piece of data in a particular scope.
  2. References must always be valid – You can’t have dangling references.

These rules are checked at compile time, meaning many potential bugs are caught before your code even runs.

fn main() {
    let mut s = String::from("hello");

    let r1 = &s;     // No problem
    let r2 = &s;     // No problem
    // let r3 = &mut s; // BIG PROBLEM - can't have mutable borrow while immutable borrows exist

    println!("{} and {}", r1, r2);
    // r1 and r2 are no longer used after this point

    let r3 = &mut s; // Now this is OK
    r3.push_str(", world!");
}

Why Ownership and Borrowing Matter

  1. Memory Safety – No dangling pointers, no null pointer dereferences.
  2. No Data Races – Rust’s borrowing rules prevent multiple threads from unsafely accessing the same data.
  3. No Garbage Collection Overhead – Memory is managed deterministically at compile time.
  4. Explicit Control – You know exactly when memory is allocated and freed.
rust ownership

Common Ownership Patterns

1. Returning Ownership

fn give_ownership() -> String {
    let s = String::from("hello");
    s  // Ownership is returned
}

2. Taking and Giving Back Ownership

fn take_and_give_back(s: String) -> String {
    s  // Ownership is returned
}

3. Using References Instead of Ownership

fn calculate_length(s: &String) -> usize {
    s.len()
}  // s goes out of scope but since it's a reference, nothing is dropped

Lifetimes

While not covered in detail here, lifetimes are another important concept that works with ownership and borrowing. They ensure that references are valid for as long as they’re needed.

Conclusion

Rust ownership and borrowing are fundamental to Rust’s approach to memory safety. While these concepts might seem restrictive at first, they enable Rust programs to be both safe and efficient. The compiler’s strict checks mean you’ll catch many potential bugs early in the development process. As you become more familiar with these concepts, you’ll find they lead to more robust and maintainable code.

Rust’s ownership model represents a significant shift from how most other languages handle memory management, but it’s this very model that gives Rust its unique advantages in performance and safety.

FAQs

What happens when ownership is moved?

  • When ownership is moved, the original variable can no longer be used. This prevents multiple variables from trying to free the same memory.​

Can I clone data to keep using the original variable?

  • Yes, using the .clone() method creates a deep copy of the data, allowing both variables to own their respective copies.​

Why does Rust restrict multiple mutable references?

  • To prevent data races, Rust enforces that only one mutable reference to a value exists at a time. This ensures safe concurrent access.​

How does borrowing help with memory safety?

  • Borrowing allows functions to access data without taking ownership, ensuring that the data remains valid and preventing dangling references.

Metana Guarantees a Job 💼

Plus Risk Free 2-Week Refund Policy ✨

You’re guaranteed a new job in web3—or you’ll get a full tuition refund. We also offer a hassle-free two-week refund policy. If you’re not satisfied with your purchase for any reason, you can request a refund, no questions asked.

Web3 Solidity Bootcamp

The most advanced Solidity curriculum on the internet!

Full Stack Web3 Beginner Bootcamp

Learn foundational principles while gaining hands-on experience with Ethereum, DeFi, and Solidity.

You may also like

Metana Guarantees a Job 💼

Plus Risk Free 2-Week Refund Policy

You’re guaranteed a new job in web3—or you’ll get a full tuition refund. We also offer a hassle-free two-week refund policy. If you're not satisfied with your purchase for any reason, you can request a refund, no questions asked.

Web3 Solidity Bootcamp

The most advanced Solidity curriculum on the internet

Full Stack Web3 Beginner Bootcamp

Learn foundational principles while gaining hands-on experience with Ethereum, DeFi, and Solidity.

Learn foundational principles while gaining hands-on experience with Ethereum, DeFi, and Solidity.

Events by Metana

Dive into the exciting world of Web3 with us as we explore cutting-edge technical topics, provide valuable insights into the job market landscape, and offer guidance on securing lucrative positions in Web3.

Subscribe to Lettercamp

We help you land your dream job! Subscribe to find out how

Spring Career Kickstart Book a call before Apr 28th to get 20% OFF!

20% OFF

Days
Hours
Minutes
Seconds

New Application Alert!

A user just applied for Metana Web3 Solidity Bootcamp. Start your application here : metana.io/apply

Get a detailed look at our Full Stack Bootcamp

Understand the goal of the bootcamp

Find out more about the course

Explore our methodology & what technologies we teach

You are downloading 2025 updated Full stack Bootcamp syllabus!

Download the syllabus to discover our Full-Stack Software Engineering Bootcamp curriculum, including key modules, project-based learning details, skill outcomes, and career support. Get a clear path to becoming a top developer.

Software Engineering Syllabus Download

"*" indicates required fields

This field is for validation purposes and should be left unchanged.