# อธิบาย Lifetime ใน Rust: Annotation, Elision และคำถามสัมภาษณ์ 2026 > คู่มือครบถ้วนเกี่ยวกับ lifetime ใน Rust สำหรับการสัมภาษณ์งาน เรียนรู้ lifetime annotation, กฎ elision, borrow checker และคำถามทางเทคนิคที่พบบ่อยพร้อมตัวอย่างโค้ดจริง - Published: 2026-08-27 - Updated: 2026-08-27 - Author: Anthony Fillion-Maillet - Reading time: 12 min --- Lifetime ใน Rust คือกลไกของ compiler ในการติดตามว่า reference ยังคงใช้งานได้นานแค่ไหน ต่างจากภาษาที่ใช้ garbage collection ที่การจัดการหน่วยความจำเกิดขึ้นใน runtime นั้น borrow checker ของ Rust จะตรวจสอบความถูกต้องของ reference ในเวลา compile ซึ่งกำจัดข้อผิดพลาดเกี่ยวกับหน่วยความจำทั้งหมดก่อนที่โค้ดจะถูกรัน > **คำตอบด่วนสำหรับสัมภาษณ์** > > Lifetime ใน Rust คือโครงสร้างในเวลา compile ที่อธิบายขอบเขตที่ reference ยังคงใช้งานได้ Borrow checker ใช้ lifetime เพื่อให้แน่ใจว่า reference จะไม่มีชีวิตยืนยาวกว่าข้อมูลที่มันชี้ไป ป้องกัน dangling pointer โดยไม่มี overhead ใน runtime ## Lifetime แทนอะไรในหน่วยความจำ Lifetime ไม่ได้เกี่ยวกับว่าค่ามีอยู่นานแค่ไหน แต่อธิบายว่า *reference* ไปยังค่ายังคงใช้งานได้นานเท่าไหร่ ทุก reference ใน Rust มี lifetime แม้ว่า annotation จะถูกละเว้น พิจารณาฟังก์ชันต่อไปนี้ที่ compiler ปฏิเสธ: ```rust // dangling_reference.rs fn create_dangling() -> &String { let s = String::from("hello"); &s // ERROR: `s` is dropped at end of function } ``` String `s` มีชีวิตอยู่เฉพาะภายใน scope ของฟังก์ชัน การ return reference ไปยังมันจะสร้าง dangling pointer เนื่องจากหน่วยความจำของ String จะถูก deallocate เมื่อฟังก์ชัน return Borrow checker จับสิ่งนี้ได้ในเวลา compile การแก้ไขต้อง return ค่าที่เป็นเจ้าของหรือให้แน่ใจว่าข้อมูลที่ถูก reference มีชีวิตยืนยาวกว่าการเรียกฟังก์ชัน: ```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("") } ``` ใน `first_word` นั้น `&str` ที่ถูก return ยืมจาก input `s` ดังนั้นมันยังคงใช้งานได้ตราบเท่าที่ `s` ยังใช้งานได้ ผู้เรียกควบคุม lifetime ของ input ## ไวยากรณ์และความหมายของ Lifetime Annotation Lifetime annotation ที่ชัดเจนใช้ไวยากรณ์ `'a`, `'b` และอื่นๆ สิ่งเหล่านี้ไม่ใช่คำสั่งไปยัง compiler เกี่ยวกับว่า reference ควรมีชีวิตอยู่นานแค่ไหน แต่อธิบายความสัมพันธ์ระหว่าง lifetime ของหลาย reference เอกสาร [Rust Reference](https://doc.rust-lang.org/reference/trait-bounds.html#lifetime-bounds) กำหนด lifetime annotation เป็นพารามิเตอร์ generic ที่จำกัดว่า reference ต้องยังคงใช้งานได้นานเท่าไหร่เมื่อเทียบกัน ```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` บอก compiler ว่า: reference ที่ถูก return จะใช้งานได้สำหรับ *ส่วนตัดกัน* ของ lifetime ของ `x` และ `y` เนื่องจาก `string2` มี lifetime สั้นกว่า `result` จึงไม่สามารถใช้หลังจาก `string2` ถูก drop หลาย lifetime ที่แตกต่างกันแสดงความสัมพันธ์ที่ซับซ้อนกว่า: ```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); } ``` ## กฎ Lifetime Elision ใน Rust 2024 Compiler ใช้กฎ elision สามข้อเพื่อสรุป lifetime เมื่อ annotation ถูกละเว้น กฎเหล่านี้ซึ่งบันทึกไว้ใน [Rustonomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html) ลด boilerplate โดยไม่เสียสละความปลอดภัย > **กฎ Elision สามข้อ** > > 1. แต่ละ reference input ได้รับพารามิเตอร์ lifetime ของตัวเอง > 2. ถ้ามี input lifetime เพียงหนึ่งตัว มันจะถูกใช้กับ reference output ทั้งหมด > 3. ถ้า `&self` หรือ `&mut self` มีอยู่ lifetime ของมันจะถูกใช้กับ reference output ทั้งหมด กฎเหล่านี้จัดการกับ pattern ที่พบบ่อยที่สุด: ```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 } ``` เมื่อ elision ล้มเหลว compiler จะต้องการ annotation ที่ชัดเจน สิ่งนี้เกิดขึ้นบ่อยที่สุดกับฟังก์ชันที่ return reference ที่ได้มาจากหลาย 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 ของ Struct และความสัมพันธ์ Outlives Struct ที่มี reference ต้องการ lifetime annotation เพื่อแสดงว่าข้อมูลที่ยืมต้องมีชีวิตยืนยาวกว่า 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` ใน `Excerpt<'a>` ผูกความถูกต้องของ struct กับ lifetime ของ `text` ที่ถูกยืม การพยายามใช้ `Excerpt` หลังจาก String ต้นทางถูก drop จะทำให้เกิด compile error สำหรับ struct ที่มีหลาย reference แต่ละตัวอาจมี lifetime ที่แตกต่างกัน: ```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 และเมื่อไหร่ควรใช้ Lifetime `'static` บ่งชี้ว่าข้อมูลมีชีวิตตลอดการทำงานของโปรแกรม String literal มี lifetime นี้เพราะถูกฝังอยู่ใน 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); ``` Pattern ที่พบบ่อยเกี่ยวข้องกับ bound `T: 'static` ซึ่งมักทำให้ developer สับสน Bound นี้หมายความว่า `T` ไม่มี reference ที่ไม่ใช่ static ไม่ใช่ว่า `T` ต้องเป็น 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 อาจมีชีวิตยืนยาวกว่าฟังก์ชันที่สร้างมัน ดังนั้นข้อมูลที่ถูกย้ายเข้าไปใน thread ต้องไม่มี reference ไปยังตัวแปร stack-local ## ข้อผิดพลาด Lifetime ที่พบบ่อยและวิธีแก้ไข หลาย pattern ทำให้ developer สะดุดอย่างสม่ำเสมอ การเข้าใจสิ่งเหล่านี้ช่วยเร่งการ debug **การ return reference ไปยังตัวแปร 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 } ``` **การเก็บ reference ใน struct ที่มี lifetime ไม่ตรงกัน:** ```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 ที่อ้างอิงตัวเอง ที่ field หนึ่ง reference field อื่น ต้องการ `Pin` กับ unsafe code หรือ crate เช่น [ouroboros](https://docs.rs/ouroboros/) โมดูล [Rust smart pointers](/technologies/rust/interview-questions/smart-pointers) ครอบคลุม pattern ขั้นสูงเหล่านี้ **Lifetime bound บน 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) สำหรับ Lifetime Generic Higher-ranked trait bounds ใช้ไวยากรณ์ `for<'a>` เพื่อแสดงว่า type ต้องทำตาม trait สำหรับ *lifetime ใดก็ได้* ไม่ใช่แค่ตัวเฉพาะ: ```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 ปรากฏบ่อยใน API ที่รับ closure และ ecosystem [Rust async/await](/blog/rust/rust-async-await-tokio-futures-concurrency) ที่ future ต้องทำงานกับ reference ของ lifetime ที่หลากหลาย ## คำถามสัมภาษณ์เกี่ยวกับ Lifetime ใน Rust การสัมภาษณ์ทางเทคนิคทดสอบความเข้าใจเกี่ยวกับ lifetime ในหลายระดับความลึก คำถามเหล่านี้ปรากฏเป็นประจำในตำแหน่ง Rust **คำถาม: ทำไมโค้ดนี้ compile ไม่ได้?** ```rust // interview_q1.rs fn get_str() -> &str { "hello" } ``` **คำตอบ:** Return type ต้องการ lifetime annotation ที่ชัดเจน แม้ว่า string literal จะมี lifetime `'static` แต่ function signature ไม่ได้แสดงสิ่งนี้ วิธีแก้: `fn get_str() -> &'static str` **คำถาม: อธิบายว่าทำไมสิ่งนี้ compile ได้และมันปลอดภัยหรือไม่:** ```rust // interview_q2.rs fn longest<'a>(x: &'a str, _y: &str) -> &'a str { x } ``` **คำตอบ:** สิ่งนี้ compile ได้เพราะ output lifetime ผูกกับ `x` เท่านั้น พารามิเตอร์ `_y` สามารถมี lifetime ใดก็ได้เนื่องจากค่าที่ return ไม่ได้ขึ้นอยู่กับมัน สิ่งนี้ปลอดภัย: ความถูกต้องของ reference ที่ return ขึ้นอยู่กับ lifetime ของ `x` เท่านั้น **คำถาม: เกิดอะไรขึ้นเมื่อพยายามเก็บ reference พร้อมกับข้อมูลที่เป็นเจ้าของ?** ```rust // interview_q3.rs struct Config<'a> { name: String, description: &'a str, } ``` **คำตอบ:** Pattern นี้ถูกต้องแต่จำกัดวิธีที่ `Config` สามารถใช้ได้ Struct ไม่สามารถมีชีวิตยืนยาวกว่าสิ่งที่ `description` reference สำหรับข้อมูลที่เป็นเจ้าของที่ต้อง reference ตัวเอง ให้พิจารณาใช้ `String` สำหรับทั้งสอง field หรือเทคนิคที่กล่าวถึงใน [Rust ownership and borrowing](/blog/rust/rust-ownership-borrowing-demystified) **คำถาม: Lifetime ทำงานอย่างไรกับ trait object?** ```rust // interview_q4.rs trait Formatter { fn format(&self, input: &str) -> String; } fn get_formatter<'a>() -> Box { // ... } ``` **คำตอบ:** Trait object มี bound lifetime `'static` โดยนัยเป็นค่าเริ่มต้น การเขียน `Box` อย่างชัดเจนอนุญาตให้ trait object มี reference ที่มี lifetime `'a` โดยไม่มี bound ที่ชัดเจน `Box` เท่ากับ `Box` ## Lifetime Variance: Covariance และ Contravariance Variance กำหนดว่า lifetime สัมพันธ์กันอย่างไรเมื่อ type ซ้อนกัน Reference ใน Rust ปฏิบัติตามกฎเหล่านี้: - `&'a T` เป็น **covariant** ใน `'a`: lifetime ที่ยาวกว่าสามารถแทนที่สั้นกว่าได้ - `&'a mut T` เป็น **invariant** ใน `T`: type ต้องตรงกันพอดี - `fn(&'a T)` เป็น **contravariant** ใน `'a`: lifetime ที่สั้นกว่าสามารถแทนที่ยาวกว่าได้ ```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 } ``` การเข้าใจ variance สำคัญเมื่อออกแบบ API generic ที่รับหรือ return reference บทความ [Rust traits and generics](/blog/rust/rust-traits-generics-advanced-guide) ครอบคลุม pattern generic ขั้นสูง ## Lifetime Annotation ในทางปฏิบัติ: ประเด็นหลัก - Lifetime อธิบายความถูกต้องของ reference ไม่ใช่การมีอยู่ของค่า Borrow checker ใช้มันเพื่อป้องกัน dangling pointer ในเวลา compile - กฎ elision จัดการกรณีส่วนใหญ่โดยอัตโนมัติ Annotation ที่ชัดเจนจำเป็นเมื่อ return reference ที่ได้มาจากหลาย input - Lifetime ของ struct แสดงความสัมพันธ์ outlives: struct ใดก็ตามที่มี reference ต้องถูก parameterize ด้วย lifetime ของ reference เหล่านั้น - Bound `'static` บน generic หมายถึง "ไม่มี reference ที่ไม่ใช่ static" ไม่ใช่ "ต้องเป็น reference" - Struct ที่อ้างอิงตัวเองต้องการการจัดการพิเศษผ่าน `Pin`, unsafe code หรือ crate ช่วยเหลือ - คำถามสัมภาษณ์มุ่งเน้นที่การเข้าใจว่าทำไมโค้ด compile ไม่ได้และ lifetime annotation เปลี่ยนการรับประกันความถูกต้องอย่างไร --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/rust/rust-lifetimes-explained-annotations-elision-interview