intermediate
+200 XP

References & Borrowing Deep Dive

Master ownership, references, and the borrow checker in Move

Lesson Syllabus

Ownership & Immutable References
🏷️

What is Ownership?

# What is Ownership? Every value in Move has exactly **one owner** at any given time. ## The Ownership Principle ```move struct Coin has drop { value: u64 } fun give_coin(coin: Coin) { // 'coin' is now owned by this function // When function ends, coin is dropped (destroyed) } fun test() { let my_coin = Coin { value: 100 }; give_coin(my_coin); // Ownership transferred! // my_coin no longer accessible here! } ``` **What happened?** 1. `test()` creates `my_coin` - it owns the Coin 2. Passing to `give_coin()` **transfers ownership** 3. `test()` no longer owns the coin 4. When `give_coin()` ends, the coin is dropped This is called **move semantics** - the coin literally moves from one owner to another.

📚

Why We Need References

# The Problem with Ownership Transfer If every function call transfers ownership, we can't use values multiple times: ```move fun calculate_score(player: Player): u64 { player.score * 2 } fun test() { let player = Player { score: 100 }; let double = calculate_score(player); // player moved! // Can't use player again - it's gone! } ``` ## The Solution: Borrowing Instead of transferring ownership, we can **borrow** the value: ```move fun calculate_score(player: &Player): u64 { // Borrow with & player.score * 2 } fun test() { let player = Player { score: 100 }; let double = calculate_score(&player); // Lend it temporarily // player is still here! Can use it again! } ``` **Analogy**: Giving someone a book (ownership transfer) vs letting them read your book (borrowing).

🔒

Immutable References (&)

# Immutable References: Read-Only Access An immutable reference `&T` gives you **read-only** access to a value. ## Key Properties 1. **No Modification**: Can only read, not write 2. **Multiple Allowed**: Can have many `&T` references at once 3. **No Ownership Transfer**: Original owner keeps the value ```move fun read_score(player: &Player): u64 { player.score // Can read // player.score = 200; // ERROR: can't modify! } ``` ## Dereferencing For primitives, use `*` to get the value: ```move fun get_double(num: &u64): u64 { *num * 2 // Dereference with * } ``` For structs, field access is automatic: ```move fun get_balance(account: &Account): u64 { account.balance // No * needed for field access! } ```

👥

Multiple Immutable Borrows

# Multiple Readers are Safe You can have **unlimited** immutable references to the same value: ```move fun test() { let value = 100; let ref1 = &value; let ref2 = &value; let ref3 = &value; // All valid! Multiple readers, no writers. } ``` ## Why This is Safe All references are read-only - no one can change the value while others are reading it. No data races possible! ## Connecting to Lesson 5 Remember `vector::borrow()`? ```move let first = vector::borrow(&scores, 0); let second = vector::borrow(&scores, 1); // Both valid! Reading different elements ``` You were creating multiple immutable references to vector elements!

Mutable References & Rules
✍️

Mutable References (&mut)

# Mutable References: Exclusive Write Access A mutable reference `&mut T` gives you **read and write** access to a value. ## Key Properties 1. **Can Modify**: Full read-write access 2. **Exclusive**: Only **ONE** `&mut` at a time 3. **No Sharing**: Can't coexist with `&` references ```move fun increase_score(player: &mut Player, points: u64) { player.score = player.score + points; // Can modify! } fun test() { let mut player = Player { score: 100 }; increase_score(&mut player, 50); // player.score is now 150 } ``` **Why `mut` twice?** - `let mut player`: Variable itself is mutable - `&mut player`: Passing a mutable reference

⚖️

The Borrow Checker Rules

# The Borrow Checker's Safety Rules Move enforces these rules to prevent data races and bugs: ## Rule 1: Multiple `&` OR One `&mut` You can have: - **Many immutable references (`&`)**, OR - **ONE mutable reference (`&mut`)** - **But NEVER both at the same time** ```move let mut x = 10; // ✅ Valid: Multiple immutable let r1 = &x; let r2 = &x; // ✅ Valid: One mutable let m1 = &mut x; // ❌ Invalid: Mixing & and &mut let r1 = &x; let m1 = &mut x; // ERROR! ``` ## Why This Matters Prevents reading a value while someone else is modifying it!

Dereferencing with *

# When to Use the Dereference Operator The `*` operator **dereferences** a reference to get the value. ## With Primitives (u64, bool, etc.) **Always use `*` when:** - Reading the value - Assigning a new value ```move fun increment(num: &mut u64) { *num = *num + 1; // Need * for both read and write } ``` ## With Structs **Field access is automatic:** ```move fun update_score(player: &mut Player, points: u64) { player.score = player.score + points; // No * needed! } ``` **But accessing the whole struct needs `*`:** ```move fun clone_player(player: &Player): Player { *player // Returns a copy of the entire struct } ```

🚨

Common Compiler Errors

# Understanding Borrow Checker Errors ## Error 1: Multiple Mutable Borrows ```move let mut x = 10; let m1 = &mut x; let m2 = &mut x; // ERROR! ``` **Fix**: Use borrows sequentially, not simultaneously. ## Error 2: Mixing Immutable and Mutable ```move let mut x = 10; let r = &x; let m = &mut x; // ERROR! ``` **Fix**: Finish using immutable borrows before creating mutable ones. ## Error 3: Borrow After Move ```move let player = Player { score: 100 }; process(player); // Ownership moved let ref = &player; // ERROR: player was moved! ``` **Fix**: Borrow instead of moving: `process(&player)`

Patterns & Best Practices
🎯

When to Use References vs Ownership

# Choosing Between References and Ownership ## Use Immutable Reference (`&T`) When: - **Reading data** without modification - **Multiple accesses** to the same value - **Keeping ownership** in the calling function ```move fun calculate_total(items: &vector<Item>): u64 { // Just reading - use & } ``` ## Use Mutable Reference (`&mut T`) When: - **Modifying in place** - **Updating without transferring ownership** ```move fun increment_score(player: &mut Player, points: u64) { // Modifying - use &mut player.score = player.score + points; } ``` ## Use Owned Value (`T`) When: - **Transferring ownership** - **Consuming/destroying** the value - **Returning** a newly created value ```move fun destroy_item(item: Item) { // Takes ownership, item is dropped } ```

🔄

Reference Patterns in Move

# Common Patterns You'll Use ## Pattern 1: Borrow to Read, Return Owned ```move fun create_summary(player: &Player): PlayerSummary { PlayerSummary { total: player.wins + player.losses, ratio: player.wins / player.losses } } ``` ## Pattern 2: Borrow Mut to Modify, Return Nothing ```move fun apply_damage(player: &mut Player, damage: u64) { player.health = player.health - damage; // No return - just modify in place } ``` ## Pattern 3: Take Ownership, Transform, Return New ```move fun evolve_character(character: Character): EvolvedCharacter { // Takes ownership, creates new type EvolvedCharacter { level: character.level + 1, power: character.power * 2 } // character is destroyed } ```

💡

Connecting the Dots

# Bringing It All Together Remember all those times you used `&` and `&mut` in Lessons 4 and 5? Now you understand **why**! ## Vector Operations (Lesson 5) ```move vector::push_back(&mut scores, 100); // ^^^^ // Modifying the vector - need &mut! let first = vector::borrow(&scores, 0); // ^ // Just reading - use & ``` ## Struct Modification (Lesson 4) ```move fun level_up(player: &mut Player) { // ^^^^ // Modifying player - need &mut! player.level = player.level + 1; } ``` ## Why These Rules Matter Without the borrow checker, you could have: - **Data races**: Reading while someone else writes - **Use-after-free**: Using a value that was destroyed - **Double-free**: Destroying the same value twice Move's borrow checker **prevents all of these at compile time**!