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

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:
- Mỗi giá trị có đúng một chủ sở hữu (owner) tại một thời điểm
- Khi owner ra khỏi scope, giá trị bị drop (dọn)
- 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ộ