昨天提到了生命週期的標記,今天我們就來看一下該怎麼手動標記生命週期
生命週期的標記長這樣 ' + 單字或者字母
大部分的人都會用 'a 來表示
我們先來解決昨天最後一個範例錯誤
> 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
其實錯誤訊息就有提示我們該怎麼加生命週期標記
我們來看一下
a 跟 b 在借用標記後面加上 'a 代表著他要活得跟 'a 一樣久
不過我們都不知道 'a 會活多久
我們來把生命週期加上去吧
當我們要在函式中使用生命週期標記時,需要加上 <'a> 表示聲明的意思
我們在返回值也會加上 'a 表示返回值的生命週期與參數們一樣,
這樣就不需要擔心無效的指向
fn main() {
    let number1 = 1;
    let number2 = 2;
    let result = introduction(&number1, &number2);
    println!("{:?}", result)
}
fn introduction<'a>(a: &'a i32, b: &'a i32) -> &'a i32 {
    if a > b {
        a
    } else {
        b
    }
}
所以說, 'a 到底能活多久?這個需要依據裡面的參數而定
當我們在 Function 聲明使用生命週期的時候,
'a 的生命週期範圍就會是參數之間重疊的區域,
也就是說,'a 的生命週期與生命週期短的參數一樣,
我們直接用上面的例子來看好了
在 introduction 兩個參數的作用域在於 main 這個 Function 中,所以兩個的生命週期是一樣的
那如果我們把 number2 以及 introduction 這個 Function 放到一個 block 中呢?
fn main() {
    let number1 = 1;
    let result;
    {
        let number2 = 2;
        result = introduction(&number1, &number2);
    }
    println!("{:?}", result)
}
fn introduction<'a>(a: &'a i32, b: &'a i32) -> &'a i32 {
    if a > b {
        a
    } else {
        b
    }
}
即便我們給他生命週期的標記,但在編譯的時候還是會出錯,為什麼呢?
> cargo run
error[E0597]: `number2` does not live long enough
  --> src/main.rs:7:41
   |
6  |         let number2 = 2;
   |             ------- binding `number2` declared here
7  |         result = introduction(&number1, &number2);
   |                                         ^^^^^^^^ borrowed value does not live long enough
8  |     }
   |     - `number2` dropped here while still borrowed
9  |
10 |     println!("{:?}", result)
   |                      ------ borrow later used here
For more information about this error, try `rustc --explain E0597`.
error: could not compile `hello_world` (bin "hello_world") due to previous error
剛剛有說到 'a 的生命週期取決於兩個參數重疊的作用域,
也就是說會以作用域小的為主
在上述的例子 'a 的作用域會跟 number2 相同,生命週期也相同
一旦出了 block 'a 與 number2 就不在了,
但其實他們應該要活到 println! 這裡
否則 result 就會指向無效的值
要怎麼解決呢?
可以把 number2 與 introduction 的 block 拿掉,
或者不要以借用的方式使用參數
如果希望參數之間的生命週期不一樣呢?我們可以使用多個生命週期
fn main() {
    let number1 = 1;
    let number2 = 2;
    let result = introduction(&number1, &number2);
    println!("{:?}", result)
}
fn introduction<'a, 'b>(a: &'a i32, b: &'b i32) -> &'a i32 {
    if a > b {
        a
    } else {
        b
    }
}
不過這樣會編譯失敗,因為 Rust 無法確定返回值會借用哪個參數,
這個錯誤訊息在說 'b 可能會活得不夠久
如果返回值借用到 b 這個參數,可能就會壞掉
> cargo run
error: lifetime may not live long enough
  --> src/main.rs:13:9
   |
9  | fn introduction<'a, 'b>(a: &'a i32, b: &'b i32) -> &'a i32 {
   |                 --  -- lifetime `'b` defined here
   |                 |
   |                 lifetime `'a` defined here
...
13 |         b
   |         ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
   |
   = help: consider adding the following bound: `'b: 'a`
error: could not compile `hello_world` (bin "hello_world") due to previous error
所以我們需要給 b 一點強心針,讓 'b 活得跟 'a 一樣久
fn main() {
    let number1 = 1;
    let number2 = 2;
    let result = introduction(&number1, &number2);
    println!("{:?}", result)
}
fn introduction<'a, 'b: 'a>(a: &'a i32, b: &'b i32) -> &'a i32 {
    if a > b {
        a
    } else {
        b
    }
}
> cargo run
2