生命週期參數(lifetime parameter)
/ 'a -> "tick-ay" /
想像你借給朋友一張寫著置物櫃號碼的紙條。這張紙條只在置物櫃還租著的時候有用;一旦租期結束,號碼就指向一個空櫃,照著它去找毫無意義。Rust 裡的參考正是這張紙條:它是一次借用,是一個指向別人所擁有之值的位址。要讓借用安全,被指的值必須活得比紙條久。生命週期參數就是讓你在函式或型別簽章中寫下「好幾個借用各自有效多久」之間關係的方式——好讓編譯器檢查沒有任何紙條活得比它的櫃子久。
生命週期以一個撇號加一個短名來命名,例如 'a(讀作「tick a」),它是個泛型參數,就和型別參數 T 一樣,只不過它代表的是一段程式碼區域而非一個型別。當你寫 fn longest<'a>(x: &'a str, y: &'a str) -> &'a str 時,你是在告訴編譯器:兩個輸入都是至少活得和某段區域 'a 一樣久的借用,而回傳的借用也活不過 'a。編譯器不會替你挑一個能讓事情成立的 'a;它檢查的是:無論你的呼叫端實際提供什麼具體區域,回傳的參考都不會逃出那段區域。'a 與 'b 這些名字本身沒有任何意義——它們只是標籤,讓你能說「這個借用和那個借用共用一段生命週期」或「這個輸出借自那個輸入,而不是另一個」。有一個內建的名字 'static,意思是在整個程式執行期間都有效的借用(字串字面值的型別就是 &'static str)。
為何重要:生命週期參數不是會改變程式行為的指令——它們不帶任何執行期成本,也完全不產生任何程式碼。它們是你陳述出來的約束,好讓借用檢查器能在編譯期證明:每一個參考都指向仍存活的記憶體。初學者常見的誤解是以為 'a 設定或控制了一個值能活多久;它並不會。值的壽命由所有權與範圍決定;'a 只是描述並關聯它的各個借用,好讓檢查器駁回那些會懸置的借用。你只在編譯器自己推不出這層關係時才需要寫它們——多數情況下省略規則會替你補上。
// 'a 說:回傳的借用活不過「兩個」輸入。 fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } let r; { let s = String::from("short-lived"); r = longest("long literal", &s); // r 可能借自 s…… } // s 在此被釋放 // println!("{r}"); // error[E0597]:`s` 存活時間不夠久
'a 並不延長任何值的壽命;它只是讓編譯器駁回一個會活得比它所指之物還久的借用。
生命週期參數描述的是借用,不是資料。寫下 'a 永遠不會讓某個值活得更久;它只陳述一條約束,再由借用檢查器去驗證。忘了這點,是初讀生命週期簽章時最常見的困惑來源。