dpway

The middle way!

[Rust][003] Foundations

Tags = [ Rust ]

rust-cheat-sheet

Chương Foundations đi sâu vào từng khái niệm, cơ chế và triết lý thiết kế của Rust

Foundations


1. Memory

a. Stack vs Heap

  • Stack:

    • Cấp phát tĩnh: Kích thước dữ liệu phải biết tại compile-time.
    • Frame-based: Mỗi hàm tạo một stack frame chứa biến cục bộ.
    • Giải phóng tự động: Khi hàm kết thúc, frame bị "pop" và bộ nhớ tự giải phóng.
    • Tối ưu hóa: Truy cập qua offset tính từ stack pointer (CPU register).

    Ví dụ Assembly (x86-64):

    ; Giả sử biến x: i32 được lưu tại [rbp - 4]
    mov dword ptr [rbp - 4], 42  ; Gán x = 42
    
  • Heap:

    • Cấp phát động: Sử dụng Box<T>, Vec<T>, String.
    • Chi phí: Cần gọi allocator (vd: malloc/free), có thể gây fragmentation.
    • Smart Pointers: Box<T> quản lý việc giải phóng qua Drop.

    Ví dụ Cấp Phát Heap:

    let x = Box::new(42); // Allocate 4 bytes trên heap
    // Khi x ra khỏi scope, Drop::drop được gọi để giải phóng.
    

b. Static Memory

  • Dữ liệu tồn tại suốt chương trình (ví dụ: static variables, chuỗi hằng).
  • Tham chiếu 'static có thể trỏ đến dữ liệu này:
static PI: f64 = 3.14159; // Static memory
let s: &'static str = "hello"; // Chuỗi hằng trong static memory

c. Alignment và Padding

  • Alignment: Yêu cầu địa chỉ bộ nhớ phải chia hết cho giá trị nhất định (vd: u64 cần 8-byte alignment).
  • Padding: Bộ nhớ đệm giữa các field để đảm bảo alignment.

Ví dụ Struct Alignment:

#[repr(C)]
struct Example {
    a: u8,   // 1 byte
    // Padding 3 bytes để alignment cho b (4-byte)
    b: u32,  // 4 bytes
}
// Kích thước struct = 1 + 3(padding) + 4 = 8 bytes

2. Ownership

a. Move Semantics

  • Di chuyển (Move): Transfer ownership, invalidate biến gốc.
  • Triển khai: Mọi type không implement Copy đều mặc định move semantics.

Ví dụ Sâu Về Move:

let s1 = String::from("hello"); // String: ptr, len, capacity
let s2 = s1; // Move semantics:
             // - s1.ptr, s1.len, s1.capacity được copy bit-wise sang s2.
             // - s1 bị mark là "moved", không thể truy cập.

b. Copy vs Clone

  • Copy: Bit-wise copy (shallow), chỉ dành cho types có kích thước cố định và không quản lý tài nguyên (vd: i32).
  • Clone: Deep copy, yêu cầu explicit call (vd: String::clone()).

Ví dụ Clone:

let s1 = String::from("hello");
let s2 = s1.clone(); // Tạo bản sao độc lập trên heap

3. Borrowing

a. Reference Rules (Compiler Checks)

  1. Không thể có cả &mut T&T cùng tồn tại.
  2. Không thể có nhiều &mut T trong cùng scope.

Ví dụ Phức Tạp:

let mut x = 42;
let r1 = &x;        // OK: shared borrow
let r2 = &x;        // OK: thêm shared borrow
// let r3 = &mut x; // Lỗi: x đang được borrow immutable

b. Borrow Checker và Lifetime Inference

  • Dataflow Analysis: Trình biên dịch phân tích luồng dữ liệu để xác định phạm vi borrow.
  • Ví dụ Lifetime Inference Tự Động:
    fn get_first(s: &String) -> &str {
        &s[0..1] // Lifetime của output được liên kết với input &String
    }
    

4. Lifetimes

a. Lifetime Annotations trong Structs

  • Struct chứa tham chiếu phải khai báo lifetime để đảm bảo dữ liệu tham chiếu tồn tại đủ lâu.
struct StrWrapper<'a> {
    data: &'a str, // Tham chiếu đến str với lifetime 'a
}

b. Lifetime Variance

  • Covariance: Cho phép thay thế lifetime dài hơn.
    • Ví dụ: &'a T là covariant với 'a.
  • Invariance: Không cho phép thay thế.
    • Ví dụ: &mut T là invariant do mutation có thể gây dangling reference.

Ví dụ Variance:

fn covariant<'a: 'b>(x: &'a str) -> &'b str {
    x // OK: 'a dài hơn 'b → covariance
}

fn invariant<'a>(x: &mut &'a str) {
    let s: &'static str = "hello";
    *x = s; // Lỗi tiềm ẩn: 'static có thể dài hơn 'a
}

5. Interior Mutability

a. Cell và RefCell

  • Cell<T>:

    • Sao chép giá trị (yêu cầu T: Copy).
    • Không có runtime checks.
    use std::cell::Cell;
    let x = Cell::new(42);
    x.set(10); // Thay đổi giá trị qua immutable reference
    
  • RefCell<T>:

    • Kiểm tra borrow rules tại runtime (có thể panic).
    • Trả về Ref hoặc RefMut để kiểm soát quyền truy cập.
    use std::cell::RefCell;
    let x = RefCell::new(42);
    let mut y = x.borrow_mut(); // Runtime borrow check
    *y = 10;
    

b. UnsafeCell và Concurrency

  • UnsafeCell<T>: Lõi của interior mutability, cho phép alias mutable references (không an toàn).
    • Được sử dụng trong Mutex, RwLock.
    use std::cell::UnsafeCell;
    let x = UnsafeCell::new(42);
    let ptr = x.get(); // Raw pointer, không an toàn
    

6. Drop và Resource Management

a. Custom Drop Logic

  • Implement trait Drop để định nghĩa cách giải phóng tài nguyên.
struct Resource {
    handle: RawFd,
}

impl Drop for Resource {
    fn drop(&mut self) {
        unsafe { libc::close(self.handle) }; // Đóng file descriptor
    }
}

b. Drop Order

  • Thứ tự drop: Biến được khai báo sau drop trước (LIFO).
  • Struct fields: Drop theo thứ tự khai báo.

Ví dụ:

struct A(String);
struct B(String);

let a = A("a".into());
let b = B("b".into());
// Drop order: b → a

7. Zero-Cost Abstractions

a. Compile-Time Optimizations

  • Monomorphization: Generic code được biên dịch thành multiple copies cho từng type cụ thể.
    fn add<T: std::ops::Add>(a: T, b: T) -> T::Output {
        a + b
    }
    // add::<i32> và add::<f64> được biên dịch riêng
    

b. Inlining và LLVM IR

  • #[inline]: Gợi ý trình biên dịch inline hàm để tối ưu tốc độ.
  • LLVM IR: Rust biên dịch sang LLVM Intermediate Representation, tận dụng tối ưu hóa mạnh mẽ.

8. Unsafe Rust và Raw Pointers

a. Raw Pointers

  • *const T*mut T: Con trỏ thô, không an toàn.
    let x = 42;
    let ptr: *const i32 = &x;
    unsafe {
        println!("{}", *ptr); // Truy cập unsafe
    }
    

b. Safe Abstraction Over Unsafe

  • Ví dụ: Triển khai Vec<T> sử dụng unsafe code nhưng expose safe API.
    pub struct Vec<T> {
        ptr: *mut T,
        len: usize,
        capacity: usize,
    }
    

Kết Luận

Chương này phơi bày triết lý thiết kế sâu sắc của Rust:

  • Kiểm soát bộ nhớ mà không cần GC, thông qua ownership/borrowing.
  • Zero-cost abstractions: Tối ưu hóa tối đa tại compile-time.
  • An toàn nhưng vẫn linh hoạt với unsafe khi cần.

Hiểu sâu các khái niệm này giúp tận dụng tối đa sức mạnh của Rust, đồng thời tránh được các pitfalls phổ biến như data races, dangling pointers, và memory leaks.