iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0

昨天我們有提到了 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

該怎麼標記,這部分我們會在明天提到


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

1 則留言

0
hello world
iT邦新手 5 級 ‧ 2023-10-05 21:38:02

幫大大補充一下:

其實標注生命週期變數的主要目的,是為了讓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 呢/images/emoticon/emoticon31.gif

我不是大大,我只是rust新手,還不到可以演講的等級,只是希望有機會幫忙推廣一下rust (?

很棒耶,感謝你們提供場地舉辦rust meetup。有機會可以相互交流一下。

好呀,有機會可以來交流一下 /images/emoticon/emoticon08.gif

我要留言

立即登入留言