# Giải Thích Lifetime trong Rust: Annotation, Elision và Câu Hỏi Phỏng Vấn 2026 > Hướng dẫn toàn diện về lifetime trong Rust cho phỏng vấn. Tìm hiểu lifetime annotation, quy tắc elision, borrow checker và các câu hỏi kỹ thuật phổ biến với ví dụ code thực tế. - Published: 2026-08-27 - Updated: 2026-08-27 - Author: Anthony Fillion-Maillet - Reading time: 12 min --- Lifetime trong Rust là cơ chế của compiler để theo dõi thời gian reference còn hợp lệ. Khác với các ngôn ngữ sử dụng garbage collection nơi quản lý bộ nhớ xảy ra trong runtime, borrow checker của Rust xác thực tính hợp lệ của reference tại thời điểm biên dịch, loại bỏ toàn bộ các lỗi bộ nhớ trước khi code được chạy. > **Trả Lời Nhanh Cho Phỏng Vấn** > > Lifetime trong Rust là một cấu trúc thời gian biên dịch mô tả phạm vi mà một reference còn hợp lệ. Borrow checker sử dụng lifetime để đảm bảo reference không bao giờ tồn tại lâu hơn dữ liệu mà nó trỏ đến, ngăn chặn dangling pointer mà không có overhead runtime. ## Lifetime Thực Sự Đại Diện Cho Gì Trong Bộ Nhớ Lifetime không nói về thời gian giá trị tồn tại. Chúng mô tả thời gian *reference* đến giá trị còn hợp lệ. Mọi reference trong Rust đều có lifetime, ngay cả khi annotation bị bỏ qua. Xem xét hàm sau mà compiler từ chối: ```rust // dangling_reference.rs fn create_dangling() -> &String { let s = String::from("hello"); &s // ERROR: `s` is dropped at end of function } ``` String `s` chỉ tồn tại trong phạm vi hàm. Trả về reference đến nó sẽ tạo dangling pointer, vì bộ nhớ của String được giải phóng khi hàm trả về. Borrow checker bắt lỗi này tại thời điểm biên dịch. Cách sửa yêu cầu trả về giá trị owned hoặc đảm bảo dữ liệu được reference tồn tại lâu hơn lời gọi hàm: ```rust // valid_return.rs // Option 1: Return owned value fn create_owned() -> String { String::from("hello") } // Option 2: Reference data that outlives the function fn first_word(s: &str) -> &str { s.split_whitespace().next().unwrap_or("") } ``` Trong `first_word`, `&str` được trả về mượn từ input `s`, nên nó còn hợp lệ chừng nào `s` còn hợp lệ. Người gọi kiểm soát lifetime của input. ## Cú Pháp và Ngữ Nghĩa Của Lifetime Annotation Lifetime annotation rõ ràng sử dụng cú pháp `'a`, `'b`, v.v. Đây không phải là hướng dẫn cho compiler về thời gian reference nên tồn tại. Chúng mô tả mối quan hệ giữa lifetime của nhiều reference. Tài liệu [Rust Reference](https://doc.rust-lang.org/reference/trait-bounds.html#lifetime-bounds) định nghĩa lifetime annotation là tham số generic ràng buộc thời gian reference phải còn hợp lệ so với nhau. ```rust // lifetime_annotations.rs // Both inputs and output share the same lifetime 'a fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let string1 = String::from("long string"); let result; { let string2 = String::from("short"); result = longest(&string1, &string2); println!("Longest: {}", result); // Valid: both strings alive } // println!("{}", result); // ERROR: string2 dropped } ``` Annotation `'a` cho compiler biết: reference được trả về sẽ hợp lệ trong *giao điểm* của lifetime `x` và `y`. Vì `string2` có lifetime ngắn hơn, `result` không thể được sử dụng sau khi `string2` bị drop. Nhiều lifetime khác nhau thể hiện mối quan hệ phức tạp hơn: ```rust // multiple_lifetimes.rs // Output tied only to first parameter's lifetime fn first_only<'a, 'b>(x: &'a str, _y: &'b str) -> &'a str { x } fn main() { let owned = String::from("owned"); let result; { let temporary = String::from("temporary"); result = first_only(&owned, &temporary); } // result still valid: only depends on owned println!("{}", result); } ``` ## Quy Tắc Lifetime Elision trong Rust 2024 Compiler áp dụng ba quy tắc elision để suy ra lifetime khi annotation bị bỏ qua. Các quy tắc này, được ghi lại trong [Rustonomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html), giảm boilerplate mà không hy sinh sự an toàn. > **Ba Quy Tắc Elision** > > 1. Mỗi reference input nhận tham số lifetime riêng > 2. Nếu có đúng một input lifetime, nó áp dụng cho tất cả reference output > 3. Nếu `&self` hoặc `&mut self` tồn tại, lifetime của nó áp dụng cho tất cả reference output Các quy tắc này xử lý hầu hết các pattern phổ biến: ```rust // elision_examples.rs // Rule 1: Each input gets own lifetime fn takes_two(x: &str, y: &str) {} // Compiler reads: fn takes_two<'a, 'b>(x: &'a str, y: &'b str) {} // Rule 2: Single input lifetime propagates to output fn first_char(s: &str) -> &str { &s[0..1] } // Compiler reads: fn first_char<'a>(s: &'a str) -> &'a str // Rule 3: &self lifetime propagates to output impl Parser { fn peek(&self) -> &Token { &self.tokens[self.position] } // Compiler reads: fn peek<'a>(&'a self) -> &'a Token } ``` Khi elision thất bại, compiler yêu cầu annotation rõ ràng. Điều này thường xảy ra nhất với các hàm trả về reference được dẫn xuất từ nhiều input: ```rust // elision_fails.rs // ERROR: Can't determine output lifetime fn ambiguous(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } } // FIX: Explicit annotation resolves ambiguity fn unambiguous<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } ``` ## Lifetime Của Struct và Mối Quan Hệ Outlives Struct chứa reference yêu cầu lifetime annotation để thể hiện rằng dữ liệu mượn phải tồn tại lâu hơn struct: ```rust // struct_lifetimes.rs struct Excerpt<'a> { text: &'a str, } impl<'a> Excerpt<'a> { // new borrows from input, so Excerpt can't outlive the source fn new(source: &'a str, start: usize, end: usize) -> Self { Excerpt { text: &source[start..end] } } // level() returns owned data, no lifetime in signature fn level(&self) -> u32 { self.text.len() as u32 / 10 } } fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let excerpt = Excerpt::new(&novel, 0, 16); println!("Excerpt: {}", excerpt.text); } // novel dropped, but excerpt already out of scope ``` `'a` trong `Excerpt<'a>` ràng buộc tính hợp lệ của struct với lifetime của `text` được mượn. Cố gắng sử dụng `Excerpt` sau khi String nguồn bị drop sẽ gây ra lỗi biên dịch. Với struct có nhiều reference, mỗi cái có thể có lifetime khác nhau: ```rust // multiple_struct_lifetimes.rs struct Comparison<'a, 'b> { baseline: &'a str, candidate: &'b str, } impl<'a, 'b> Comparison<'a, 'b> { fn new(baseline: &'a str, candidate: &'b str) -> Self { Comparison { baseline, candidate } } fn baseline_only(&self) -> &'a str { self.baseline } } ``` ## Lifetime 'static và Khi Nào Sử Dụng Lifetime `'static` chỉ ra dữ liệu tồn tại trong toàn bộ thời gian chương trình chạy. String literal có lifetime này vì chúng được nhúng trong binary: ```rust // static_lifetime.rs let s: &'static str = "I live forever"; // Constants are implicitly 'static const CONFIG_VERSION: &str = "2.0.0"; // Thread-safe globals require 'static static COUNTER: AtomicU64 = AtomicU64::new(0); ``` Một pattern phổ biến liên quan đến bound `T: 'static`, thường gây nhầm lẫn cho developer. Bound này có nghĩa `T` không chứa reference non-static, không phải `T` phải là reference: ```rust // static_bound.rs use std::thread; fn spawn_task(data: T) { thread::spawn(move || { // data moved into thread, must live independently println!("Processing in thread"); }); } fn main() { let owned = String::from("owned data"); spawn_task(owned); // OK: String is 'static (owns its data) let reference = "borrowed"; // spawn_task(reference); // OK: &'static str } ``` Thread có thể tồn tại lâu hơn hàm tạo ra nó, nên dữ liệu được move vào thread không được chứa reference đến biến stack-local. ## Các Lỗi Lifetime Phổ Biến và Cách Giải Quyết Một số pattern liên tục làm developer vấp ngã. Hiểu được chúng giúp tăng tốc debug. **Trả về reference đến biến local:** ```rust // error_local_ref.rs fn bad_split(text: &str, delimiter: char) -> (&str, &str) { let parts: Vec<&str> = text.split(delimiter).collect(); (parts[0], parts[1]) // OK: parts[i] borrow from text, not from Vec } fn truly_bad() -> &str { let local = String::from("local"); &local // ERROR: local dropped at end of function } ``` **Lưu trữ reference trong struct với lifetime không khớp:** ```rust // error_struct_lifetime.rs struct Cache { data: String, // view: &str, // ERROR: needs lifetime parameter } // Self-referential structs require unsafe or crates like ouroboros struct CacheWithView<'a> { data: String, view: Option<&'a str>, // Can't point to self.data safely } ``` Struct tự tham chiếu, nơi một field reference field khác, yêu cầu `Pin` với code unsafe hoặc crate như [ouroboros](https://docs.rs/ouroboros/). Module [Rust smart pointers](/technologies/rust/interview-questions/smart-pointers) đề cập các pattern nâng cao này. **Lifetime bound trên trait implementation:** ```rust // trait_lifetime_bounds.rs trait Processor { fn process<'a>(&self, input: &'a str) -> &'a str; } struct Prefixer { prefix: String, } impl Processor for Prefixer { // Can't return &format!(...) - would be temporary fn process<'a>(&self, input: &'a str) -> &'a str { input // Must return input or part of it } } ``` ## Higher-Ranked Trait Bounds (HRTBs) Cho Lifetime Generic Higher-ranked trait bounds sử dụng cú pháp `for<'a>` để thể hiện rằng một type phải thỏa mãn trait cho *bất kỳ* lifetime, không chỉ một cái cụ thể: ```rust // hrtb.rs use std::fmt::Debug; // F must be callable with any lifetime 'a fn apply_to_refs(f: F) where F: for<'a> Fn(&'a str) -> &'a str, { let owned = String::from("test"); let result = f(&owned); println!("{}", result); } fn identity(s: &str) -> &str { s } fn main() { apply_to_refs(identity); } ``` HRTBs thường xuất hiện trong các API nhận closure và hệ sinh thái [Rust async/await](/blog/rust/rust-async-await-tokio-futures-concurrency) nơi future phải làm việc với reference của các lifetime khác nhau. ## Câu Hỏi Phỏng Vấn Về Lifetime Rust Phỏng vấn kỹ thuật kiểm tra sự hiểu biết về lifetime ở nhiều mức độ. Các câu hỏi này thường xuất hiện trong các vị trí Rust. **Câu hỏi: Tại sao code này không biên dịch được?** ```rust // interview_q1.rs fn get_str() -> &str { "hello" } ``` **Trả lời:** Kiểu trả về cần lifetime annotation rõ ràng. Mặc dù string literal có lifetime `'static`, function signature không thể hiện điều này. Cách sửa: `fn get_str() -> &'static str`. **Câu hỏi: Giải thích tại sao đoạn code này biên dịch được và liệu có an toàn không:** ```rust // interview_q2.rs fn longest<'a>(x: &'a str, _y: &str) -> &'a str { x } ``` **Trả lời:** Đoạn code này biên dịch được vì lifetime output chỉ ràng buộc với `x`. Tham số `_y` có thể có lifetime bất kỳ vì giá trị trả về không phụ thuộc vào nó. Điều này an toàn: tính hợp lệ của reference trả về chỉ phụ thuộc vào lifetime của `x`. **Câu hỏi: Điều gì xảy ra khi cố lưu reference cùng với dữ liệu owned?** ```rust // interview_q3.rs struct Config<'a> { name: String, description: &'a str, } ``` **Trả lời:** Pattern này hợp lệ nhưng ràng buộc cách `Config` có thể được sử dụng. Struct không thể tồn tại lâu hơn bất cứ thứ gì mà `description` reference. Với dữ liệu owned cần reference đến chính nó, hãy cân nhắc sử dụng `String` cho cả hai field, hoặc các kỹ thuật được đề cập trong [Rust ownership and borrowing](/blog/rust/rust-ownership-borrowing-demystified). **Câu hỏi: Lifetime tương tác với trait object như thế nào?** ```rust // interview_q4.rs trait Formatter { fn format(&self, input: &str) -> String; } fn get_formatter<'a>() -> Box { // ... } ``` **Trả lời:** Trait object có bound lifetime `'static` ngầm định theo mặc định. Viết `Box` rõ ràng cho phép trait object chứa reference với lifetime `'a`. Không có bound rõ ràng, `Box` bằng với `Box`. ## Lifetime Variance: Covariance và Contravariance Variance xác định cách lifetime liên quan khi type được lồng nhau. Reference trong Rust tuân theo các quy tắc sau: - `&'a T` là **covariant** trong `'a`: lifetime dài hơn có thể thay thế ngắn hơn - `&'a mut T` là **invariant** trong `T`: type phải khớp chính xác - `fn(&'a T)` là **contravariant** trong `'a`: lifetime ngắn hơn có thể thay thế dài hơn ```rust // variance.rs fn covariant_example() { let s: &'static str = "static"; let r: &str = s; // OK: 'static lives longer than any 'a } fn invariant_example() { let mut vec: Vec<&'static str> = vec!["a"]; // let s = String::from("local"); // vec.push(&s); // ERROR: &s is not &'static str } ``` Hiểu variance quan trọng khi thiết kế API generic nhận hoặc trả về reference. Bài viết [Rust traits and generics](/blog/rust/rust-traits-generics-advanced-guide) đề cập các pattern generic nâng cao. ## Lifetime Annotation Trong Thực Tế: Điểm Chính - Lifetime mô tả tính hợp lệ của reference, không phải sự tồn tại của giá trị. Borrow checker sử dụng chúng để ngăn dangling pointer tại thời điểm biên dịch. - Quy tắc elision xử lý hầu hết các trường hợp tự động. Annotation rõ ràng trở nên cần thiết khi trả về reference được dẫn xuất từ nhiều input. - Lifetime của struct thể hiện mối quan hệ outlives: bất kỳ struct nào chứa reference phải được tham số hóa bởi lifetime của các reference đó. - Bound `'static` trên generic có nghĩa "không chứa reference non-static," không phải "phải là reference." - Struct tự tham chiếu yêu cầu xử lý đặc biệt thông qua `Pin`, code unsafe, hoặc crate hỗ trợ. - Câu hỏi phỏng vấn tập trung vào việc hiểu tại sao code không biên dịch được và cách lifetime annotation thay đổi đảm bảo tính hợp lệ. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/rust/rust-lifetimes-explained-annotations-elision-interview