昨天我們有提到了 Rust 的生命週期
接下來就要進入比較困難的部分了
有時候 Rust 沒辦法判斷變數的生命週期,我們就得手動幫他們加上標記
我們來看下面的例子,當我們在 Function 中用借用的方式帶入到參數中,且回傳值是參照參數的話,就會出現錯誤訊息
fn main() {
let number1 = 1;
let number2 = 2;
let result = calculator(&number1, &number2);
}
fn calculator(a: &i32, b: &i32) -> &i32 {
if a > b {
a
} else {
b
}
}
這邊的錯誤訊息為,回傳值應該要有生命週期的標記
> cargo run
error[E0106]: missing lifetime specifier
--> src/main.rs:8:38
|
8 | fn introduction(a: &i32, b: &i32) -> &i32 {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
help: consider introducing a named lifetime parameter
|
8 | fn introduction<'a>(a: &'a i32, b: &'a i32) -> &'a i32 {
| ++++ ++ ++ ++
For more information about this error, try `rustc --explain E0106`.
error: could not compile `hello_world` (bin "hello_world") due to previous error
為什麼會有這個訊息出現?
是因為 Rust 無法判斷返回值的生命週期,所以需要標記,
Rust 需要知道回傳值所借用的是哪個參數,並藉此判斷他的生命週期
Function 有分成三種狀況
Function 如果只有一個參數的話,我們就不需要加上標記,
狀況比較單純,所以 Rust 有辦法判斷返回值的生命週期
Rust 知道返回值的生命週期是跟著這唯一的參數,
所以我們不需要特別標記,它也能推斷出來
fn main() {
let number1 = 1;
let number2 = 2;
let result = introduction(&number1);
println!("{:?}", result)
}
fn introduction(a: &i32) -> &i32 {
a
}
> cargo run
1
返回值沒有參考參數的狀況就更簡單了,
Rust 會知道當 Function 結束時,返回值的生命週期也會結束,
所以我們不需要特別標記
fn main() {
let number1 = 1;
let number2 = 2;
let result = introduction(&number1);
println!("{:?}", result)
}
fn introduction(a: &i32) -> i32 {
10
}
> cargo run
10
跟上個例子一樣,
Rust 會知道當 Function 結束時,返回值的生命週期也會結束
所以我們不需要特別標記
fn main() {
let number1 = 1;
let number2 = 2;
let result = introduction(&number1, &number2);
println!("{:?}", result)
}
fn introduction(a: &i32, b: &i32) -> i32 {
10
}
> cargo run
10
fn main() {
let number1 = 1;
let number2 = 2;
let result = introduction(&number1, &number2);
println!("{:?}", result)
}
fn introduction(a: &i32, b: &i32) -> i32 {
let c = a + b;
c
}
> cargo run
3
跟本文最一開始的範例一樣,
如果返回值有參照到參數的話,就需要標記,
因為 Rust 不知道返回值是跟隨著哪個參數的,
我們必須要明確地告訴他
fn main() {
let number1 = 1;
let number2 = 2;
let result = introduction(&number1, &number2);
println!("{:?}", result)
}
fn introduction(a: &i32, b: &i32) -> &i32 {
if a > b {
a
} else {
b
}
}
編譯的時候就會出錯了,它期望明確的生命週期標記提示它
> cargo run
error[E0106]: missing lifetime specifier
--> src/main.rs:9:38
|
9 | fn introduction(a: &i32, b: &i32) -> &i32 {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
help: consider introducing a named lifetime parameter
|
9 | fn introduction<'a>(a: &'a i32, b: &'a i32) -> &'a i32 {
| ++++ ++ ++ ++
For more information about this error, try `rustc --explain E0106`.
error: could not compile `hello_world` (bin "hello_world") due to previous error
該怎麼標記,這部分我們會在明天提到
幫大大補充一下:
其實標注生命週期變數的主要目的,是為了讓rust進行所有權的檢查,不能借用已丟失的值。
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1 // 參考 s1
} else {
s1 // 參考 s2
}
} // 回傳的結果不能活的比s1或s2久
fn main() {
let a = String::from("Ning"); // a 活整個 main
let result; // result 活整個 main
{
let b = String::from("Chang"); // b 只活在block裡
result = longer(a.as_str(), b.as_str()); // result 看a, b哪個短
println!("The longer string is {}", result); // b還活著,所以ok
} // 離開這b就死了
println!("The longer string is {}", result); // 借不到b
}
因為result有可能借用 a 也有可能借用 b,所以a, b要活的比result久,result才能正確依所有權規則借用。
上例呼叫longer時,回傳給 result的生命週期 就會代入 s1 或 s2 較短者,哪怕原本的result是活整個main,但在block裡result被賦值時,就跟著一個生命週期檢查器,檢查result不能活比b久。
p.s. 一個變數不用標注生命週期變數是rust知道怎麼幫我們標。
感謝大大的補充!
我目前在五倍紅寶石程式教育機構任職,每週禮拜二晚上都會舉辦默默會
不定期會有主題演講,想問問大大有沒有意願來默默會演講 Rust 呢
我不是大大,我只是rust新手,還不到可以演講的等級,只是希望有機會幫忙推廣一下rust (?
很棒耶,感謝你們提供場地舉辦rust meetup。有機會可以相互交流一下。
好呀,有機會可以來交流一下