langShift

Ownership & Memory Model

Deep dive into Rust's ownership system, borrowing rules, and memory management, contrasting with JavaScript's garbage collection

Ownership & Memory Model

📖 Learning Objectives

Understand Rust's ownership system, its most fundamental concept. We will start from JavaScript's memory management and gradually understand how Rust ensures memory safety through compile-time checks.


🎯 Memory Management Comparison

JavaScript's Garbage Collection

JavaScript uses an automatic garbage collection mechanism:

正在加载...

Rust's Ownership System

Rust uses the ownership system to manage memory at compile time:

正在加载...

Memory Management Differences

  1. Automatic vs Manual: JavaScript automatic garbage collection, Rust compile-time checks
  2. Reference Semantics: JavaScript objects passed by reference, Rust by ownership transfer
  3. Memory Safety: JavaScript runtime checks, Rust compile-time guarantees
  4. Performance: JavaScript has garbage collection overhead, Rust zero-cost abstractions

🔄 Ownership Rules

Rust's Ownership Rules

Rust has three core ownership rules:

正在加载...

🔗 Borrowing & References

Immutable Borrows

正在加载...

Mutable Borrows

正在加载...

Borrowing Rules

正在加载...

📏 Lifetimes

Lifetime Annotations

正在加载...

🎯 Ownership & JavaScript Comparison

Data Passing Comparison

正在加载...

🎯 Exercises

Exercise 1: Ownership Transfer

Analyze the following code and identify where ownership transfer occurs:

fn main() {
let s1 = String::from("hello");
let s2 = s1;
let s3 = s2.clone();
println!("s2: {}", s2);
println!("s3: {}", s3);
}
View Answer
  1. let s2 = s1; - Ownership of s1 moves to s2
  2. let s3 = s2.clone(); - s2 is cloned, s3 gets ownership of new data
  3. s2 is still valid because clone creates new data

Exercise 2: Borrowing Rules

Fix the borrowing error in the following code:

fn main() {
let mut data = vec![1, 2, 3];
let ref1 = &data;
let ref2 = &mut data;
println!("{:?}, {:?}", ref1, ref2);
}
View Answer
fn main() {
let mut data = vec![1, 2, 3];
let ref1 = &data;
let ref2 = &data; // Changed to immutable reference
println!("{:?}, {:?}", ref1, ref2);
// Or use immutable reference first, then mutable reference
let mut data = vec![1, 2, 3];
let ref1 = &data;
println!("{:?}", ref1);
let ref2 = &mut data; // Now a mutable reference can be created
ref2.push(4);
println!("{:?}", ref2);
}

Exercise 3: Lifetimes

Add the correct lifetime annotations to the following function:

fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
View Answer
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}

📝 Summary

In this chapter, we deeply learned about Rust's ownership system:

  1. Ownership Rules: Each value has one owner, and the value is dropped when the owner goes out of scope.
  2. Borrowing System: Borrow data through references, avoiding ownership transfer.
  3. Borrowing Rules: At any given time, you can have either one mutable reference or multiple immutable references.
  4. Lifetimes: Ensure references are valid for their duration.
  5. Comparison with JavaScript: Rust compile-time checks vs JavaScript runtime garbage collection.

Key Takeaways

  • The ownership system is at the core of Rust's memory safety.
  • The borrowing system allows using data without transferring ownership.
  • The lifetime system ensures references are always valid.
  • These concepts are checked at compile time with zero runtime overhead.

Common Pitfalls

  1. Use after move: Value cannot be used after being moved.
  2. Simultaneous borrows: Cannot have mutable and immutable borrows simultaneously.
  3. Dangling references: Returning a reference to a local variable.
  4. Lifetime mismatch: Reference lifetimes do not match.

Next Steps

In the next chapter, we will learn about Rust's concurrency and asynchronous programming models, and how to safely handle multi-threading and asynchronous operations.


Continue Learning: Concurrency & Asynchronous Model