1. 為什麼需要 Lifetime Elision
昨天學到每當函數有引用參數時,就要標註 'a 等生命週期,但如果每個地方都手動標註,程式會變得冗長。
Rust 編譯器其實能根據一些規則自動推導生命週期,這稱為 lifetime elision(生命週期省略規則)。
2. 三大自動推導規則
Rust 編譯器根據三個原則判斷要不要自動加 'a:
(1)每個參數的引用都會各自獲得一個生命週期。
(2)如果只有一個參數,那麼輸出的生命週期會與該參數相同。
(3)如果有多個參數但其中有 &self 或 &mut self,輸出生命週期與該物件一致。
3. 範例:單一參數時自動推導
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
fn main() {
let s = String::from("hello world");
println!("{}", first_word(&s));
}
雖然這個函數的正確生命週期寫法是:
fn first_word<'a>(s: &'a str) -> &'a str
但因為它符合只有一個參數的規則,所以 'a 可以省略。
輸出:
hello
4. 多參數時的限制
當有多個引用參數時,Rust 無法自動判斷誰比較久:
fn longest(s1: &str, s2: &str) -> &str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
這段會報錯:
error[E0106]: missing lifetime specifier
因為編譯器不知道要讓回傳值跟哪個參數綁定,必須改寫為:
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
5. 與 struct、impl 搭配使用
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention: {}", announcement);
self.part
}
}
fn main() {
let text = String::from("Rust makes memory safety fun!");
let first = text.split('.').next().unwrap();
let excerpt = ImportantExcerpt { part: first };
println!("{}", excerpt.announce_and_return_part("Read this:"));
}
輸出:
Attention: Read this:
Rust makes memory safety fun!
這裡 &self 幫助編譯器推導出回傳值的生命週期,因此不需額外註記 'a。
6. 學習心得與補充
今天的內容讓我真正理解為什麼昨天那些 'a 有時需要、但有時可以省略。原本我以為生命週期標註只是麻煩的語法,但其實 Rust 編譯器的自動推導能力相當強大。透過三條省略規則,大部分情況下根本不需要手動標註,只有在多個參考來源或複雜邏輯中才要明確標出。我覺得這設計兼顧了安全與可讀性,需要時可以精確控制,不需要時又能保持簡潔。