ข้ามไปยังเนื้อหา

Lifetimes

Lifetime คือแนวคิดที่บอก compiler ว่า reference จะยังคง valid อยู่นานแค่ไหน ใน Rust ทุก reference มี lifetime แต่ในหลายกรณี compiler สามารถอนุมานได้เองโดยไม่ต้องเขียนอย่างชัดเจน

จุดประสงค์หลักคือการป้องกัน dangling reference — situation ที่ reference ชี้ไปยังหน่วยความจำที่ถูก free ไปแล้ว

Lifetime parameter เขียนด้วย apostrophe ตามด้วยชื่อ เช่น 'a, 'b และต้องประกาศใน angle bracket เหมือน generic:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}

'a ที่นี่หมายความว่า “return value มีชีวิตอยู่อย่างน้อยนานเท่ากับ reference ที่สั้นกว่าระหว่าง x และ y

เมื่อ struct เก็บ reference จะต้องมี lifetime annotation:

struct Excerpt<'a> {
text: &'a str,
}

นี่บอก compiler ว่า instance ของ Excerpt ต้องมีชีวิตอยู่ไม่นานกว่า text ที่ชี้ไป

ในหลายกรณีทั่วไป Rust มี elision rules ที่ช่วยให้คุณละ lifetime annotation ได้ เช่น:

// คุณเขียนแบบนี้:
fn first_word(s: &str) -> &str { ... }
// Compiler เห็นเป็นแบบนี้:
fn first_word<'a>(s: &'a str) -> &'a str { ... }

Elision rules ครอบคลุมกรณีที่พบบ่อยที่สุด ทำให้ code สะอาดขึ้นโดยไม่สูญเสียความปลอดภัย

'static คือ lifetime พิเศษที่หมายความว่า reference มีชีวิตอยู่ตลอด duration ของ program:

let s: &'static str = "I have a static lifetime.";

String literal ทุกอันมี 'static lifetime เพราะถูก embed อยู่ใน binary ของ program

Playground ด้านล่างแสดง function longest พร้อม lifetime annotation และ struct Excerpt ที่เก็บ reference — ลองดูว่า borrow checker รับประกันความถูกต้องของ reference อย่างไร

// A function with a lifetime annotation:
// the returned reference lives at least as long as the shorter of x or y.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// A struct that holds a reference — must annotate the lifetime.
struct Excerpt<'a> {
text: &'a str,
}
impl<'a> Excerpt<'a> {
fn announce(&self) -> &str {
self.text
}
}
fn main() {
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
println!("longest: {}", result);
}
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find '.'");
let excerpt = Excerpt { text: first_sentence };
println!("excerpt: {}", excerpt.announce());
}
Lifetime annotation ใน Rust มีไว้เพื่ออะไร?
ใน `fn longest<'a>(x: &'a str, y: &'a str) -> &'a str` annotation `'a` รับประกันอะไร?
Compiler สามารถ infer lifetime โดยอัตโนมัติโดยไม่ต้องเขียน annotation ได้เมื่อใด?
การที่ lifetime annotation เป็น 'compile-time only' หมายความว่าอะไร?