This is efficient because they live entirely on the stack (no heap allocation)
After copying, both variables remain valid
let x =5;let y = x;// Copy happens (not a move)println!("x = {}, y = {}", x, y);// ✅ Both valid!
🤖 AI co-pilot technique: understanding moves
When you encounter move errors, ask your AI assistant for help:
Effective prompts: - “Explain this Rust move error in simple terms: [paste error message]” - “Why does Rust move String but copy i32?” - “How do I use a value after it’s been moved?” - “What’s the difference between Copy and Clone in Rust?”
Pro tip: The Rust compiler error messages are incredibly helpful on their own - read them carefully before asking AI!
Scope and automatic cleanup
Every variable has a scope: the region of code where it’s valid
The original owner retains ownership, so the value won’t be dropped
fn calculate_length(s:&String) ->usize{ s.len()}// s goes out of scope, but it doesn't own the String, so nothing is droppedfn main() {let my_string =String::from("hello");let len = calculate_length(&my_string);// Borrow, don't moveprintln!("{} has length {}", my_string, len);// ✅ Still valid!}
A reference’s scope ends at its last use, not at the closing brace
This makes many common patterns “just work”
letmut s =String::from("hello");let r1 =&s;// Immutable borrowlet r2 =&s;// Another immutable borrowprintln!("{}, {}", r1, r2);// r1 and r2 last used here - scope endslet r3 =&mut s;// ✅ OK: no immutable borrows are activeprintln!("{}", r3);
🤖 AI co-pilot technique: debugging borrow checker errors
The borrow checker is your friend, but it takes practice. Use AI to accelerate your learning:
Effective prompts: - “Explain this borrow checker error: [paste full error message]” - “Why can’t I borrow this variable as mutable? [paste code]” - “How do I fix ‘cannot borrow as mutable more than once’?” - “What’s a dangling reference and how does Rust prevent them?”
Best practice: Read the error message first, understand the rule being violated, then ask AI for deeper insight or alternative solutions.
This is a critical security vulnerability in C/C++ (use-after-free)
Rust makes dangling references impossible at compile time
fn dangle() ->&String{// ❌ Won't compile!let s =String::from("hello");&s // ERROR: s will be dropped, but we're returning a reference to it}// s goes out of scope and is dropped, but the reference would point to freed memory
The fix: Return the owned value, not a reference:
fn no_dangle() ->String{// ✅ Ownership transfers to callerlet s =String::from("hello"); s // Move ownership out}
Introduction to lifetimes
Lifetimes are Rust’s way of ensuring references are always valid
Every reference has a lifetime - the scope for which it’s valid
Usually, the compiler can infer lifetimes automatically
Sometimes you need to annotate them explicitly with 'a, 'b, etc.
Function returns a reference that came from one of multiple input references
Structs contain references (struct must not outlive the referenced data)
Multiple references with complex relationships where the compiler can’t infer
Good news: In most code, lifetimes are inferred automatically thanks to lifetime elision rules
🤖 AI co-pilot technique: understanding lifetimes
Lifetimes can be confusing. Use AI to build intuition:
Effective prompts: - “Explain Rust lifetimes like I’m coming from Python” - “Why does this code need a lifetime annotation? [paste code]” - “What does ‘a mean in this function signature? [paste signature]“ - ”How do I fix ’lifetime may not live long enough’?” - “When can I omit lifetime annotations in Rust?”
Pro tip: Use Rust Playground to experiment. Change code, see errors, ask AI to explain the errors.
Lifetimes in structs
struct Excerpt<'a>{ text:&'astr,// This reference must be valid as long as the Excerpt exists}fn main() {let novel =String::from("Call me Ishmael. Some years ago...");let first_sentence = novel.split('.').next().unwrap();let excerpt = Excerpt { text: first_sentence };println!("{}", excerpt.text);}// excerpt and novel both go out of scope here (OK: excerpt doesn't outlive novel)
When a struct contains a reference, we must annotate the lifetime
'a says: “this struct can’t live longer than the data it references”
What ownership gives you: - Memory safety without garbage collection - No data races (guaranteed at compile time) - Predictable performance (no GC pauses) - Zero-cost abstractions (compile-time checks, zero runtime overhead)
The trade-off: - Steeper learning curve (you’re learning now!) - Fight the borrow checker (initially - then you’ll think differently) - More upfront thinking about data ownership
Career insight: Once you understand ownership, you’ll write better code in every language. You’ll think about memory and references more carefully even in Python or JavaScript.
Real-world performance wins
Examples of Rust’s ownership system delivering real results:
Discord: Switched from Go to Rust, reduced latency spikes from seconds to milliseconds (10x improvement)
Dropbox: Rewrote sync engine in Rust, reduced memory usage by 2x, improved performance
npm: Rewrote CPU-heavy services in Rust, 10x performance improvement
Microsoft: Using Rust in Windows to prevent 70% of security vulnerabilities
The common thread: No garbage collector + memory safety = high performance + security
Week 11 preview: structs, enums, and pattern matching - Creating custom data types with struct - Rust’s powerful enum type - Pattern matching with match - AI-assisted data modeling strategies
For now: Focus on mastering ownership and borrowing. These concepts underpin everything else in Rust!
Questions?
Remember: - The borrow checker is your friend (even when it feels like your enemy) - Compiler error messages are incredibly helpful - read them carefully - Use AI assistants to deepen understanding, not to skip learning - Fighting with Rust now makes you a better programmer forever
Office hours: Available on Microsoft Teams - reach out anytime!