Lifetimes are Rust's mechanism for ensuring that references are always valid — they never point to memory that has been freed (a dangling reference). The borrow checker tracks how long each value lives and how long each reference lives to guarantee that no reference outlives the data it points to.
Lifetime là cơ chế của Rust để đảm bảo rằng các tham chiếu luôn hợp lệ — chúng không bao giờ trỏ tới bộ nhớ đã bị giải phóng (dangling reference). Bộ kiểm tra mượn (borrow checker) theo dõi bao lâu mỗi giá trị sống và bao lâu mỗi tham chiếu sống để đảm bảo tham chiếu không tồn tại lâu hơn dữ liệu mà nó trỏ tới.
1fn main() {2 // This is what lifetimes prevent:3 let r;4 {5 let x = 5;6 r = &x;7 // x goes out of scope here and is dropped8 }9 // println!("{r}"); // ERROR: x does not live long enough10 // r would be a dangling reference to freed stack memory1112 // This is fine: r lives shorter than x13 let x = 5;14 let r = &x;15 println!("{r}"); // 5 — x is still alive16}In most cases Rust infers lifetimes automatically through elision rules. You only need to write lifetime annotations when the compiler cannot determine the relationship between lifetimes.
Trong hầu hết các trường hợp, Rust suy ra lifetime tự động thông qua các quy tắc bỏ qua. Bạn chỉ cần viết annotation lifetime khi trình biên dịch không thể xác định mối quan hệ giữa các lifetime.
Lifetime annotations do not change how long a reference lives — they describe the relationship between the lifetimes of multiple references. They start with a tick and are typically short lowercase letters like 'a or 'b.
Annotation lifetime không thay đổi bao lâu một tham chiếu sống — chúng mô tả mối quan hệ giữa lifetime của nhiều tham chiếu. Chúng bắt đầu bằng dấu nháy đơn và thường là chữ cái thường ngắn như 'a hay 'b.
1// &i32 — a reference (compiler infers lifetime)2// &'a i32 — a reference with an explicit lifetime 'a3// &'a mut i32 — a mutable reference with lifetime 'a45// This function needs a lifetime annotation because it6// returns a reference and the compiler cannot tell whether7// it comes from x or from y8fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {9 if x.len() > y.len() { x } else { y }10}1112fn main() {'a trong longest không có nghĩa là cả hai tham số phải sống cùng thời gian — nó có nghĩa là tham chiếu trả về sẽ hợp lệ không lâu hơn tham chiếu nào sống ngắn hơn.
You need lifetime annotations in function signatures when a function takes multiple references and returns a reference — the compiler needs to know which input the returned reference relates to.
Bạn cần annotation lifetime trong chữ ký hàm khi hàm nhận nhiều tham chiếu và trả về một tham chiếu — trình biên dịch cần biết tham chiếu được trả về liên kết với đầu vào nào.
1// Returns reference to x — lifetime tied to x only2fn first_word<'a>(s: &'a str) -> &'a str {3 let bytes = s.as_bytes();4 for (i, &byte) in bytes.iter().enumerate() {5 if byte == b' ' {6 return &s[0..i];7 }8 }9 s10}1112// When returning a reference only tied to one parameter,The Rust compiler has three elision rules that let you omit lifetime annotations in common situations. If after applying the rules there are still references without a lifetime, the compiler reports an error.
Trình biên dịch Rust có ba quy tắc bỏ qua (elision rules) cho phép bạn bỏ qua annotation lifetime trong các tình huống phổ biến. Nếu sau khi áp dụng các quy tắc, vẫn còn tham chiếu không có lifetime, trình biên dịch sẽ báo lỗi.
1// You write:2fn foo(x: &str, y: &str) -> &str { x }34// Compiler expands to:5fn foo<'a, 'b>(x: &'a str, y: &'b str) -> &str { x }6// Return still has no lifetime — rules 2 and 3 apply nextRule 2 — One Input Parameter Gives Its Lifetime to Output
Quy Tắc 2 — Một Tham Số Đầu Vào Thì Truyền Lifetime Cho Đầu Ra
1// You write:2fn first(s: &str) -> &str { s }34// Compiler expands to (rule 1 then rule 2):5fn first<'a>(s: &'a str) -> &'a str { s }6// Works! Only one input reference, so its lifetime propagates to outputRule 3 — Method with &self: self Gives Its Lifetime to Output
Quy Tắc 3 — Phương Thức với &self: self Truyền Lifetime Cho Đầu Ra
1struct Important<'a> {2 part: &'a str,3}45impl<'a> Important<'a> {6 // You write:7 fn announce(&self, msg: &str) -> &str {8 self.part9 }10 // Compiler expands (rule 1, then rule 3 — &self wins):11 // fn announce<'b>(&'a self, msg: &'b str) -> &'a str { self.part }12}1314fn main() {15 let novel = String::from("Call me Ishmael. Some years ago...");16 let first_sentence = novel.split('.').next().unwrap();17 let imp = Important { part: first_sentence };18 println!("{}", imp.announce("Attention!"));19}Hầu hết các hàm và phương thức Rust hàng ngày sử dụng bỏ qua lifetime. Bạn chỉ cần annotation tường minh khi trình biên dịch báo lỗi không rõ ràng.
If a struct holds a reference you must declare a lifetime annotation on the struct. This ensures the struct cannot outlive the data it references.
Nếu một struct chứa một tham chiếu, bạn phải khai báo annotation lifetime trên struct. Điều này đảm bảo rằng struct không tồn tại lâu hơn dữ liệu được tham chiếu.
1// Struct that holds a reference — needs lifetime annotation2#[derive(Debug)]3struct Excerpt<'a> {4 text: &'a str,5}67impl<'a> Excerpt<'a> {8 fn level(&self) -> i32 { 3 }910 fn announce_and_return(&self, msg: &str) -> &str {11 println!("Attention: {msg}");12 self.text // lifetime elision rule 3 applies hereThe 'static lifetime is the longest possible lifetime. A 'static reference lives for the entire runtime of the program. String literals are 'static because they are stored in the program binary.
Lifetime 'static là lifetime dài nhất có thể. Tham chiếu 'static tồn tại trong suốt toàn bộ thời gian chạy của chương trình. String literals là 'static vì chúng được lưu trong binary của chương trình.
1// String literals have 'static lifetime2let s: &'static str = "I live forever";34// Static variables also have 'static lifetime5static GREETING: &str = "Hello";67// 'static bound means: the type contains no non-static references8// (does NOT mean it lives forever — just that it COULD)9fn takes_static<T: 'static>(val: T) {10 println!("Got a static-safe value");11}12You can combine lifetimes and generic trait bounds. The bound T: 'a means the type T must live at least as long as the lifetime 'a.
Bạn có thể kết hợp lifetime và ràng buộc trait generic. Ràng buộc 'a có nghĩa là kiểu T phải tồn tại ít nhất là lâu bằng lifetime 'a.
1use std::fmt::Display;23// T must implement Display AND must live at least as long as 'a4fn longest_with_announcement<'a, T>(5 x: &'a str,6 y: &'a str,7 ann: T,8) -> &'a str9where10 T: Display,11{12 println!("Announcement: {ann}");1// This does NOT compile — classic dangling reference2// fn dangle() -> &String {3// let s = String::from("hello");4// &s // s is dropped at end of function — reference invalid!5// }67// Fix: return the owned value8fn no_dangle() -> String {9 let s = String::from("hello");10 s // ownership moves out, no dangling11}1213fn main() {14 let s = no_dangle();15 println!("{s}");16}1struct Config<'a> {2 host: &'a str,3 port: u16,4}56fn main() {7 let config;8 {9 let host = String::from("localhost");10 config = Config { host: &host, port: 8080 };11 println!("{}:{}", config.host, config.port); // fine here12 // host dropped at end of this block13 }14 // println!("{}", config.host); // ERROR: host dropped, config dangling15}- Add lifetime annotations when the compiler asks — do not guess ahead
Thêm annotation lifetime khi trình biên dịch yêu cầu — đừng đoán trước
- Prefer owned types (String, Vec) in structs to avoid lifetime complexity
Ưu tiên các kiểu sở hữu (String, Vec) trong struct để tránh phức tạp về lifetime
- Use 'static sparingly — often 'static is not what you actually need
Dùng 'static một cách thận trọng — thường 'static không phải là thứ bạn cần
- The borrow checker is your ally — it prevents you from writing code with undefined behavior
Bộ kiểm tra mượn là bạn của bạn — nó ngăn bạn viết code có hành vi undefined
Nếu bạn thấy mình đang chiến đấu với lifetime, hãy xem xét việc tái cấu trúc để sử dụng dữ liệu được sở hữu thay vì tham chiếu. Đây thường là giải pháp đúng đắn hơn.
Key Takeaways
Điểm Chính
- Lifetimes ensure references remain valid for their entire useLifetime đảm bảo tham chiếu hợp lệ trong suốt thời gian sử dụng
- Lifetime elision rules handle most cases automaticallyQuy tắc bỏ qua lifetime xử lý tự động hầu hết trường hợp
- Structs holding references must declare lifetime parametersStruct chứa tham chiếu phải khai báo tham số lifetime
- 'static lifetime means the reference lives for the entire programLifetime 'static nghĩa là tham chiếu tồn tại suốt chương trình
Practice
Test your understanding of this chapter
Why does the longest function require a lifetime annotation on its return type?
Tại sao hàm longest yêu cầu annotation lifetime trên kiểu trả về?
Which lifetime elision rule covers a method that takes &self and returns a reference?
Quy tắc bỏ qua lifetime nào áp dụng cho phương thức nhận &self và trả về một tham chiếu?
Lifetime annotations change how long a reference is valid by extending the lifetime of the underlying data.
Annotation lifetime thay đổi bao lâu một tham chiếu hợp lệ bằng cách kéo dài lifetime của dữ liệu bên dưới.
A struct that holds a reference field must declare a lifetime parameter so the compiler can guarantee the struct does not outlive the data it references.
Một struct chứa trường tham chiếu phải khai báo tham số lifetime để trình biên dịch đảm bảo struct không tồn tại lâu hơn dữ liệu mà nó tham chiếu.
Declare a lifetime parameter on a struct
Khai báo tham số lifetime trên một struct
#[derive(Debug)] struct Excerpt<> { text: &'a str, }