Chuyển đến nội dung chính

Bài 3: Ownership, Borrowing & Lifetimes

Ownership rules, move semantics, Copy trait. References, borrowing rules, mutable references. Lifetime annotations, lifetime elision, 'static lifetime. Borrow checker.

💻 Lập trình — Bài 3 Bài 3: Ownership, Borrowing & Lifetimes

Rust: Từ Cơ bản đến Nâng cao

Phần 1: Rust Fundamentals

xdev.asia

1. Ownership Rules

fn main() {
    // Rule 1: Mỗi value có đúng 1 owner
    // Rule 2: Chỉ có 1 owner tại mọi thời điểm
    // Rule 3: Khi owner ra khỏi scope, value bị drop

    let s1 = String::from("hello");
    let s2 = s1; // s1 bị MOVE vào s2
    // println!("{}", s1); // ❌ Error: value borrowed after move

    // Clone — deep copy
    let s3 = s2.clone();
    println!("{} {}", s2, s3); // ✅ OK

    // Copy trait — stack-only data (integers, bool, char, tuples of Copy types)
    let x = 5;
    let y = x; // Copy, không phải move
    println!("{} {}", x, y); // ✅ OK
}

// Ownership transfer qua function
fn takes_ownership(s: String) {
    println!("{}", s);
} // s bị drop ở đây

fn gives_ownership() -> String {
    String::from("hello")
}

2. Borrowing & References

fn main() {
    let s = String::from("hello");

    // Immutable reference (&T) — nhiều cùng lúc OK
    let len = calculate_length(&s);
    println!("{} has length {}", s, len); // s vẫn valid

    // Mutable reference (&mut T) — chỉ 1 tại mỗi thời điểm
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s); // "hello, world"

    // ❌ Không thể có &mut và & cùng lúc
    // let r1 = &s;
    // let r2 = &mut s; // Error: cannot borrow as mutable
}

fn calculate_length(s: &String) -> usize {
    s.len()
} // s là reference nên không drop value

fn change(s: &mut String) {
    s.push_str(", world");
}

3. Lifetimes

// Lifetime annotation — chỉ ra reference sống bao lâu
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

// Lifetime trong struct — struct không thể sống lâu hơn reference bên trong
struct Config<'a> {
    name: &'a str,
    value: &'a str,
}

impl<'a> Config<'a> {
    fn display(&self) -> String {
        format!("{}={}", self.name, self.value)
    }
}

// 'static lifetime — sống suốt chương trình
let s: &'static str = "I live forever";

// Lifetime elision rules (compiler tự suy):
// 1. Mỗi reference parameter có lifetime riêng
// 2. Nếu chỉ 1 input lifetime → gán cho tất cả output
// 3. Nếu có &self → lifetime của self gán cho output
fn first_word(s: &str) -> &str {
    // Compiler tự thêm: fn first_word<'a>(s: &'a str) -> &'a str
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[..i];
        }
    }
    s
}

4. Borrow Checker Patterns

// Non-Lexical Lifetimes (NLL) — Rust 2021+
fn main() {
    let mut data = vec![1, 2, 3];
    let first = &data[0]; // immutable borrow
    println!("{}", first); // last use of `first`
    data.push(4); // ✅ OK — NLL biết `first` không còn dùng
}

// Splitting borrows — borrow different fields
struct Point {
    x: f64,
    y: f64,
}

fn update(point: &mut Point) {
    let x = &mut point.x;
    let y = &mut point.y;
    *x += 1.0;
    *y += 1.0;
}

Bài tiếp theo: Structs, Enums & Pattern Matching.