Skip to main content
Rust Programming
CHAPTER 10 Beginner

Ownership and Borrowing

Updated: May 18, 2026
5 min read

# CHAPTER 10

Ownership and Borrowing

1. Chapter Introduction

Welcome to the most important chapter in this entire course. Ownership is Rust's defining feature. It is the magic mechanism that allows Rust to guarantee memory safety and thread safety without relying on a slow Garbage Collector. If you don't understand Ownership, you cannot write Rust. It will feel like the compiler is constantly fighting you. Let's demystify it completely.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Understand the 3 Rules of Ownership.
  • Differentiate between Stack and Heap memory.
  • Understand Move semantics (why a = b invalidates b).
  • Use the .clone() method.
  • Pass complex data into functions without destroying it.

3. Stack vs Heap Memory

To understand Ownership, you must understand where data lives.
  • The Stack: Fast, organized memory. Data must have a known, fixed size at compile time (like integers i32 or booleans). When a function ends, Stack data is instantly popped off and deleted.
  • The Heap: Slower, massive memory. Used for data that can grow or shrink (like a user's text String). The OS finds a free space, stores the data, and gives your program a Pointer (the address) to find it.

*When you are done with Heap memory, it MUST be cleaned up, or your server runs out of RAM (Memory Leak).* Java uses a Garbage Collector to clean it. C requires you to manually call free(). Rust uses Ownership.

4. The 3 Rules of Ownership

Rust's compiler (specifically the Borrow Checker) strictly enforces these three rules:
  1. 1. Each value in Rust has a variable that is called its *owner*.
  1. 2. There can only be ONE owner at a time.
  1. 3. When the owner goes out of scope (e.g., the function ends), the value will be automatically dropped (deleted from memory).

5. Move Semantics (Transferring Ownership)

Let's look at simple Stack data (Integers).
rust
123
let x = 5;
let y = x; // Makes a copy of 5
println!("x: {}, y: {}", x, y); // Works perfectly!

Now let's look at Heap data (Strings).

rust
1234567
fn main() {
    let s1 = String::from("Hello"); // s1 owns the String on the Heap
    let s2 = s1; // Ownership is TRANSFERRED (Moved) to s2!

    // println!("{}", s1); // ERROR! s1 is no longer valid!
    println!("{}", s2); // Works!
}

Why did this happen? If s1 and s2 both pointed to the same Heap memory, when the function ends, Rust would try to delete the memory twice (a "Double Free" vulnerability, which crashes systems). To prevent this, Rust enforces Rule #2: Only ONE owner. When we wrote s2 = s1, s1 handed over ownership and immediately became invalid.

Visualizing Ownership Transfer: s1 (owner of "Hello") -> let s2 = s1; -> s1 ❌ (Invalid) -> s2 ✅ (New Owner)

6. Cloning (Deep Copy)

If you truly want two separate copies of a String on the Heap, you must explicitly tell Rust to duplicate the data using .clone().
rust
1234
let s1 = String::from("Hello");
let s2 = s1.clone(); // Creates a brand new block of Heap memory!

println!("s1 = {}, s2 = {}", s1, s2); // Both work!

*(Warning: Cloning is slow. Use it sparingly.)*

7. Ownership and Functions

Passing a variable to a function transfers ownership!
rust
123456789101112
fn main() {
    let my_string = String::from("Rust");
    
    // Ownership of the string is moved INTO the function!
    take_ownership(my_string); 
    
    // println!("{}", my_string); // ERROR! my_string no longer owns the data.
}

fn take_ownership(s: String) {
    println!("I now own: {}", s);
} // 's' goes out of scope here. The Heap memory is deleted!

Wait, if passing data to a function destroys our access to it, how do we ever reuse variables?! We don't want to pass ownership. We just want to let the function *look* at the data temporarily. This is called Borrowing.

8. Mini Project: Memory-Safe String Manager

rust
123456789101112
fn main() {
    let mut message = String::from("Access");
    
    // We can move ownership back and forth, but it's tedious
    message = take_and_give_back(message);
    println!("I got it back! {}", message);
}

fn take_and_give_back(s: String) -> String {
    println!("Processing: {}", s);
    s // Return ownership back to the caller
}

9. Common Mistakes

  • Assuming Strings act like Integers: You can do let y = x; with integers because they live on the Stack and copy instantly. You cannot do this with String, Vector, or custom Structs without moving ownership.
  • Using a variable after moving it: This is the #1 error beginners face. The compiler will say "value borrowed here after move". You must either .clone() it, or use References (next chapter).

10. Best Practices

  • Respect the Compiler: When the compiler gives you an ownership error, don't just throw .clone() at it to shut it up. Think about *who* should actually own the data.

11. Exercises

  1. 1. Create a String inside main.
  1. 2. Assign it to a new variable.
  1. 3. Try to println! the first variable. Read the compiler error carefully.
  1. 4. Fix the code using .clone().

12. MCQs with Answers

Question 1

Which of the following is NOT one of Rust's Ownership rules?

Question 2

Integers and Booleans are stored where in memory?

Question 3

Dynamic, growable data like String is stored where?

Question 4

What happens when you do let s2 = s1; where s1 is a String?

Question 5

Why does Rust invalidate s1 in the previous scenario?

Question 6

What method forces Rust to duplicate Heap data so both variables are valid?

Question 7

If you pass a String into a function print_word(s: String), what happens to the String in the calling function?

Question 8

What happens to Heap data when its owner goes out of scope?

Question 9

Which of the following data types Implements the "Copy" trait, meaning it copies instead of moving?

Question 10

What is the name of the Rust compiler feature that enforces ownership?

13. Interview Questions

  • Q: Explain Rust's Ownership rules and how they replace traditional Garbage Collection.
  • Q: What is the difference between a Copy and a Move in Rust? Provide examples of data types for each.

14. FAQs

  • Is ownership slow? No! Ownership checks happen entirely during *Compile Time*. At runtime, your program runs at maximum speed with zero overhead.

15. Summary

Ownership is the architectural masterpiece of Rust. By enforcing that every piece of heap memory has exactly one owner, Rust safely deletes memory the exact millisecond the owner goes out of scope. This prevents memory leaks and security vulnerabilities. However, constantly handing ownership back and forth to functions is exhausting.

16. Next Chapter Recommendation

There must be a better way to let a function look at a string without taking ownership of it! In Chapter 11: References and Slices, we will learn how to "Borrow" data using the & symbol.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·