
Lifetime là một trong những khái niệm khó nhất khi học Rust. Về cơ bản, lifetime đảm bảo rằng mọi tham chiếu (reference) trong chương trình luôn hợp lệ trong phạm vi mà bạn sử dụng chúng, không bao giờ trỏ đến vùng nhớ đã được giải phóng. Khác với các ngôn ngữ có garbage collector như Java, Python hay JavaScript, Rust không để runtime tự dọn dẹp bộ nhớ. Thay vào đó, compiler kiểm tra các quy tắc sở hữu (ownership) và tuổi thọ tham chiếu tại thời điểm biên dịch, trước khi chương trình bao giờ chạy.

Vấn đề mà lifetime giải quyết
Trong hầu hết ngôn ngữ lập trình, con trỏ hoặc tham chiếu có thể trỏ đến dữ liệu đã bị xóa, gây ra lỗi truy cập bộ nhớ ngoài vùng hợp lệ (dangling reference). Rust giải quyết vấn đề này bằng cách yêu cầu mọi tham chiếu phải có một “thời gian sống” rõ ràng. Nếu tham chiếu A phụ thuộc vào dữ liệu của tham chiếu B, compiler phải biết rằng B tồn tại lâu hơn A.
Ví dụ đơn giản: một chuỗi ký tự được tạo trong hàm nội, tham chiếu đến nó được trả về cho hàm bên ngoài. Nếu hàm nội kết thúc trước khi hàm bên ngoài dùng xong chuỗi, dữ liệu sẽ bị hủy — và tham chiếu trở thành dangling. Rust compiler sẽ từ chối biên dịch chương trình đó.
Cú pháp annotation lifetime
Lifetime annotation không thay đổi tuổi thọ thực tế của tham chiếu, mà chỉ giúp compiler hiểu mối quan hệ giữa các tham chiếu. Cú pháp sử dụng dấu ' theo sau là tên lifetime:
fn longest(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
Trong ví dụ trên, 'a nghĩa là: “tham chiếu trả về sẽ sống ít nhất là ngắn hơn trong hai tham chiếu đầu vào x hoặc y.” Điều này đảm bảo rằng kết quả trả về luôn trỏ đến dữ liệu còn hợp lệ sau khi hàm kết thúc.

Lifetime trong struct
Khi struct chứa tham chiếu, bạn phải khai báo lifetime cho cả struct và các trường tham chiếu:
struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
let novel = String::from("Call me Ishmael.");
let excerpt = ImportantExcerpt {
part: &novel[..12],
};
}
Ở đây 'a gắn lifetime cho tham chiếu part với struct ImportantExcerpt, nghĩa là struct không thể tồn tại lâu hơn dữ liệu mà part tham chiếu đến.
Lifetime elision — quy tắc suy luận tự động
Trong nhiều trường hợp, Rust có thể tự suy luận lifetime mà không cần annotation tường minh. Ba quy tắc elision phổ biến:
- Mỗi tham chiếu đầu vào đều được gán một lifetime riêng.
- Nếu có một tham chiếu đầu vào duy nhất, lifetime đó được gán cho tất cả đầu ra.
- Nếu tham số hàm là
&selfhoặc&mut self, lifetime của self được gán cho tất cả đầu ra.
Nhờ các quy tắc này, bạn thường không cần viết lifetime annotation trong các hàm đơn giản. Chỉ khi mối quan hệ giữa tham chiếu phức tạp hơn, compiler mới yêu cầu bạn chỉ định rõ ràng.
‘static lifetime
Lifetime 'static nghĩa là dữ liệu sống trong suốt thời gian chạy của chương trình. Chuỗi ký tự literal trong Rust có lifetime 'static bởi vì nội dung được nhúng trực tiếp vào file binary:
let s: &'static str = "Đây là chuỗi sống suốt đời chương trình";
Bạn cũng có thể dùng 'static với generic để ràng buộc rằng một kiểu phải chứa tham chiếu sống mãi, hoặc kiểu đó bản thân nó sống mãi. Ví dụ, trait ToString yêu cầu kiểu triển khai có thể chuyển thành String.
Lỗi thường gặp khi mới học
- Quên annotation khi cần: Khi hàm trả về tham chiếu từ một trong hai đầu vào, compiler không thể tự suy luận và yêu cầu annotation rõ ràng.
- Lifetime không khớp: Truyền tham chiếu có lifetime ngắn hơn yêu cầu. Ví dụ, tham chiếu từ khối
{ }nội không thể thoát ra ngoài khối đó. - Nhầm lẫn với tham chiếu chuỗi:
&strcó thể là tham chiếu đến String trên heap, không phải dữ liệu static. Không nhầm lẫn với&'static str.
Tại sao lifetime quan trọng?
Lifetime là trái tim của hệ thống đảm bảo an toàn bộ nhớ Rust. Nhờ lifetime:
- Không bao giờ có lỗi dangling pointer khi chương trình chạy.
- Không bao giờ có double free (giải phóng cùng một vùng nhớ hai lần).
- Không bao giờ có data race trong đa luồng, vì tham chiếu luôn rõ ràng ai sở hữu dữ liệu.
- Compiler cung cấp thông báo lỗi chi tiết, chỉ ra chính xác vị trí và mối quan hệ lifetime cần chỉnh sửa.
Dù ban đầu gây khó chịu, việc hiểu và sử dụng thành thạo lifetime giúp bạn viết code Rust an toàn, hiệu suất cao mà không cần debugger hay runtime kiểm tra. Đó là một trong những lý do chính khiến Rust được yêu thích trong phát triển hệ thống, trình duyệt, công cụ dòng lệnh và cả nhân hệ điều hành.
Tham khảo thêm: Validating References with Lifetimes — The Rust Programming Language, Lifetimes (Nomicon).
