iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0

昨天提到了生命週期的標記,今天我們就來看一下該怎麼手動標記生命週期

生命週期標記

生命週期的標記長這樣 ' + 單字或者字母

大部分的人都會用 '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 'anumber2 就不在了,

但其實他們應該要活到 println! 這裡

否則 result 就會指向無效的值

要怎麼解決呢?

可以把 number2introduction 的 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

上一篇
Day 20 - 生命週期 part 2
下一篇
Day 22 - 生命週期 part 4
系列文
成為程式界的 F1 賽車手,用 30 天認識 Rust 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言