← Rust EnglishChapter 03 of 13

Ownership

## Learning Objectives - Understand Rust's ownership rules - Master borrowing with references - Learn lifetime basics - Avoid common ownership pitfalls ## What is Ownership? Ownership is Rust's unique system for managing memory. Every value has a single owner, and when the owner goes out of scope, the value is dropped. ## Ownership Rules 1. Each value has exactly one owner 2. There can only be one owner at a time 3. When the owner goes out of scope, the value is dropped ```rust fn main() { let s1 = String::from("hello"); // s1 owns the string let s2 = s1; // Ownership moves to s2 // println!("{}", s1); // Error: s1 is no longer valid println!("{}", s2); // OK: s2 owns the value } ``` ## Move Semantics ### Strings vs Literals ```rust // String (heap-allocated) - MOVES let s1 = String::from("hello"); let s2 = s1; // s1 is moved to s2 // s1 is now invalid // String literals (&str) - COPIES ( Clone trait) let s1 = "hello"; let s2 = s1; // s1 is copied to s2 println!("{} {}", s1, s2); // Both valid ``` ### Integer Types - Always Copy ```rust let x = 5; let y = x; // Copy (i32 implements Copy) println!("{} {}", x, y); // Both valid ``` ## Clone and Copy ### Clone (Deep Copy) ```rust let s1 = String::from("hello"); let s2 = s1.clone(); // Deep copy of heap data println!("{} {}", s1, s2); // Both valid ``` ### Copy Trait (Stack Data) ```rust // Types that implement Copy (stack-only): // - All integer types // - All floating-point types // - Boolean // - Char // - Tuple of Copy types // - Array of Copy types let x = (1, 2, 3); let y = x; // Copy, both valid ``` ## Ownership and Functions ### Passing Values to Functions ```rust fn main() { let s = String::from("hello"); takes_ownership(s); // s's value moves into function // println!("{}", s); // Error: s is invalid let x = 5; makes_copy(x); // x is copied (i32 is Copy) println!("{}", x); // OK: x is still valid } fn takes_ownership(s: String) { println!("{}", s); } // s is dropped here fn makes_copy(x: i32) { println!("{}", x); } // x is dropped here ``` ### Return Values and Scope ```rust fn main() { let s1 = gives_ownership(); // Function returns ownership let s2 = String::from("hello"); let s3 = takes_and_returns(s2); // s2 moves in, s3 is returned } fn gives_ownership() -> String { let s = String::from("hello"); s // Returns and moves to caller } fn takes_and_returns(s: String) -> String { s // Returns and moves to caller } ``` ## Borrowing ### References A reference lets you access a value without taking ownership. ```rust fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); // Pass reference to s1 println!("{} has length {}", s1, len); // s1 still valid } fn calculate_length(s: &String) -> usize { s.len() } // s goes out of scope, but NOT dropped ``` ### Mutable References ```rust fn main() { let mut s = String::from("hello"); change(&mut s); println!("{}", s); } fn change(s: &mut String) { s.push_str(", world"); } ``` ### Borrowing Rules 1. One mutable reference OR any number of immutable references 2. References must always be valid (no dangling references) ```rust let mut s = String::from("hello"); let r1 = &s; // OK let r2 = &s; // OK: multiple immutable refs // let r3 = &mut s; // Error: cannot borrow mutably while immutable refs exist println!("{} {}", r1, r2); // r1 and r2 no longer used after this point let r3 = &mut s; // OK: immutable refs are done r3.push_str(" world"); ``` ## Lifetimes ### What are Lifetimes? Lifetimes are annotations that help the compiler ensure references are valid. ```rust fn main() { let r; { let x = 5; r = &x; // Error: x doesn't live long enough } println!("{}", r); } ``` ### Lifetime Annotations ```rust // &'a means reference lives at least as long as lifetime 'a fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let s1 = String::from("long string"); let result; { let s2 = String::from("xyz"); result = longest(s1.as_str(), s2.as_str()); println!("Longest: {}", result); } // result would be invalid here if used } ``` ### Lifetime Elision The compiler uses rules to infer lifetimes in common cases: ```rust // These are equivalent: fn first_word(s: &str) -> &str { } fn first_word<'a>(s: &'a str) -> &'a str { } // Rule 1: Each reference parameter gets its own lifetime // Rule 2: If there's exactly one input lifetime, it's applied to output // Rule 3: If there's a &self or &mut self, it's applied to output ``` ### Static Lifetime `'static` means the reference lives for the entire program: ```rust let s: &'static str = "I live forever"; ``` String literals have `'static` lifetime. ## The Slice Type Slices are references to a portion of a collection: ```rust let s = String::from("hello world"); let hello = &s[0..5]; // Same as &s[..5] let world = &s[6..11]; // Same as &s[6..] // String slice fn first_word(s: &str) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return &s[0..i]; } } &s[..] } ``` ## Summary - Each value has exactly one owner - When ownership moves, the original variable is invalidated - Copy types (primitives) are copied instead of moved - Clone creates a deep copy - References borrow values without taking ownership - Mutable reference allows modification of borrowed value - Only one mutable reference OR multiple immutable references - Lifetimes ensure references are valid as long as needed - Slices are references to portions of data

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →