Rust Ownership — khi compiler là người giữ chìa khóa bộ nhớ của bạn

Phong

Hồi mới học Rust, mình nhớ cái cảm giác "sao compiler nó khó ưa quá vậy". Mượn một biến xong là nó la, gán giá trị này qua kia cũng la. Cảm giác như compiler đứng đằng sau nhắc: "Ê, mi đang làm gì vậy?".

Rồi một hôm mình chợt nhận ra: đó chính là điểm mạnh của Rust.

Ownership — một giá trị, một chủ

Rust không có garbage collector (GC) như Java, Go, hay C#. Cũng không có cơ chế free như C hay delete như C++. Vậy Rust làm sao để dọn bộ nhớ?

Câu trả lời là Ownership — một tập hợp luật được compiler kiểm tra ngay lúc biên dịch.

Ba luật cơ bản:

  1. Mỗi giá trị có đúng một chủ sở hữu (owner) tại một thời điểm
  2. Khi owner ra khỏi scope, giá trị bị drop (dọn)
  3. Có thể chuyển ownership qua assignment hoặc function call
let s1 = String::from("hello");
let s2 = s1;          // s1 move vào s2
// println!("{}", s1); // ❌ Lỗi! s1 không còn owner

Lúc đầu mình thấy cái này bất tiện. Nhưng rồi mình nhận ra: không còn bug use-after-free, không còn double-free, không còn memory leak ngẫu nhiên nữa. Mấy cái lỗi này ám ảnh dân C/C++ bao nhiêu năm thì Rust giải quyết gọn ngay lúc compile.

Borrowing — mượn tạm, đừng chiếm

Đương nhiên không ai code kiểu move qua move lại mãi được. Rust có giải pháp: borrowing (mượn tham chiếu).

fn tinh_do_dai(s: &String) -> usize {
    s.len()
} // s ra scope, nhưng không drop vì chỉ mượn

let greeting = String::from("Xin chào");
let len = tinh_do_dai(&greeting); // mượn tham chiếu
println!("{} có {} ký tự", greeting, len); // ✅ vẫn dùng được

Có hai kiểu mượn:

  • &T — immutable reference, đọc được nhưng không sửa
  • &mut T — mutable reference, được sửa nhưng chỉ một người được mượn tại một thời điểm

Quy tắc vàng: hoặc nhiều immutable reference, hoặc một mutable reference. Không cả hai cùng lúc. Nhờ đó Rust loại bỏ được data race ngay lúc compile — một điều không compiler nào khác làm được.

So sánh với GC

Khía cạnh Java / Go (GC) Rust (Ownership)
Khi nào dọn Khi GC chạy (không xác định) Khi owner ra scope (xác định)
Performance Có stop-the-world pause Không pause, deterministic
Memory overhead GC tracking overhead Zero-cost abstractions
Học curve Thấp — không cần nghĩ về memory Cao — phải hiểu ownership
Data race protection Không (cần synchronized/lock) Có ngay lúc compile

Mình thấy cái hay nhất của Ownership là nó ép bạn suy nghĩ về quyền sở hữu dữ liệu ngay từ lúc thiết kế. Bạn không thể lười được — compiler sẽ không cho qua nếu bạn quy hoạch ownership sai.

Điều này làm code Rust thường sạch hơn, rõ ràng hơn về ai là chủ của dữ liệu nào, ai được quyền sửa cái gì.

Kết

Sau một thời gian dùng Rust, mình thấy cái compiler nó không khó ưa — nó tận tâm là đằng khác. Nó không để bạn viết code ẩu rồi sáng hôm sau lên production mới vỡ lẽ.

Nếu bạn chưa thử Rust, hãy bắt đầu bằng cách viết một CLI tool nhỏ. Cảm giác lần đầu compile thành công mà không lỗi ownership — thiệt sự rất đã.


📋 Phụ lục thuật ngữ

  • Ownership — mô hình sở hữu bộ nhớ của Rust, mỗi giá trị có đúng một chủ sở hữu tại một thời điểm
  • Borrowing — cơ chế mượn tham chiếu tạm thời, không chuyển ownership
  • Data race — lỗi xảy ra khi hai thread truy cập cùng một dữ liệu và ít nhất một thread ghi, không có cơ chế đồng bộ