rust--什么是生命周期?

// 什么是生命周期?
// 參考: https://doc.rust-lang.org/book/second-edition/ch10-03-lifetime-syntax.html
// 參考: https://stackoverflow.com/questions/31609137/why-are-explicit-lifetimes-needed-in-rust


// 核心: 生命周期只跟引用/借用有關, 如果不是引用/借用, 那么就不存在生命周期的說法,
//       因為非引用/借用都必然會產生所有權轉移, 所有權轉移會跳出scope的限制.


// 生命周期作用在function、method、trait 和 struct 中,
// 編譯器默認情況下會為每個引用類型的參數自動補充不同(為每個引用單獨增加一個'a/'b/'c)的生命周期,
// 只有出現它無法識別的情況時, 才會報錯并要求讓你自己來填寫生命周期.


// 什么情況下編譯器會自動補充生命周期?
// 1. 當參數含有多個引用, 且返回值類型不是引用時, 編譯器會自動為每個引用添加一個不同的聲明周期.
//    fn print(status: &i32, msg: &str) -> Message {};
//    編譯器自動補充生命周期
//    fn print<'a, 'b>(status: &'a i32, msg: &'b i32) -> Message {};
fn multi_reference_parameters_and_return_value_is_not_reference_type() {

    #[allow(dead_code)]
    #[derive(Debug)]
    struct Message {
        status: i32,
        msg: String
    }

    // 不寫生命周期
    #[allow(unused_variables)]
    fn print1(status: &i32, msg: &str) -> Message {
        if status == &200 {
            Message {status: 200, msg: String::from("ok!")}
        } else {
            Message {status: 404, msg: String::from("page not found!")}
        }
    };

    // 寫生命周期
    #[allow(unused_variables)]
    fn print2<'a, 'b>(status: &'a i32, msg: &'b str) -> Message {
        if status == &200 {
            Message {status: 200, msg: String::from("ok!")}
        } else {
            Message {status: 404, msg: String::from("page not found!")}
        }
    }

    print1(&200, "ok!");
    print1(&404, "page not found!");

    print2(&200, "ok!");
    print2(&404, "page not found!");
}


// 2. 當參數只含有一個引用類型, 那么編譯器會自動補充生命周期, 如果返回值也是一個引用類型, 那么編譯器也會自動給引用類型的返回值添加生命周期.
//    fn print(msg: &str) -> &str {}
//    編譯器自動補充生命周期
//    fn print<'a>(msg: &'a str) -> &'a str {};
fn only_one_reference_parameter_and_return_value_type_is_reference_too() {

    // 不寫生命周期
    #[allow(unused_variables)]
    fn print1(status: i32, msg: &str) -> &str {
        if status == 200 {
            println!("{}", msg);
            msg
        } else {
            let s = "custom message";
            println!("{}", s);
            s
        }
    };

    // 寫生命周期
    #[allow(unused_variables)]
    fn print2<'a>(status: i32, msg: &'a str) -> &'a str {
        if status == 200 {
            println!("{}", msg);
            msg
        } else {
            let s = "custom message";
            println!("{}", s);
            s
        }
    }

    print1(200, "ok!");
    print1(404, "page not found!");

    print2(200, "ok!");
    print2(404, "page not found!");
}


// 3. 當參數中含有 &self 或 &mut self 時, 如果返回值也是一個引用類型, 那么編譯器會自動給引用類型的返回值添加生命周期.
//    struct ImportantExcerpt<'a> {
//        part: &'a str,
//    }
//    impl<'a> ImportantExcerpt<'a> {
//        fn announce_and_return_part(&self, announcement: &str) -> &str {
//            println!("Attention please: {}", announcement);
//            self.part
//        }
//    }
//    編譯器自動補充生命周期
//    struct ImportantExcerpt<'a> {
//        part: &'a str,
//    }
//    impl<'a> ImportantExcerpt<'a> {
//        fn announce_and_return_part(&'a self, announcement: &'a str) -> &'a str {
//            println!("Attention please: {}", announcement);
//            self.part
//        }
//    }
fn lifetime_on_method() {
    struct Message<'a> {
        id: &'a str,
        msg: &'a str,
    }

    // 不寫生命周期
    impl<'a> Message<'a> {
        fn get_msg_by_id1(&self, id: &str) -> &str {
            if self.id == id {
                println!("lifetime_on_method: {} {}", self.id, self.msg);
                self.msg
            } else {
                println!("lifetime_on_method: {} {}", id, self.msg);
                self.msg
            }
        }
    }
    let m = Message {id: "200", msg: "ok!"};
    m.get_msg_by_id1("200");
    m.get_msg_by_id1("404");

    // 寫生命周期
    impl<'a> Message<'a> {
        fn get_msg_by_id2(&'a self, id: &'a str) -> &'a str {
            if self.id == id {
                println!("lifetime_on_method: {} {}", self.id, self.msg);
                self.msg
            } else {
                println!("lifetime_on_method: {} {}", id, self.msg);
                self.msg
            }
        }
    }

    let m = Message {id: "200", msg: "ok!"};
    m.get_msg_by_id2("200");
    m.get_msg_by_id2("404");
}


// 什么情況下編譯器不會自動補充生命周期?
// 當參數含有大于一個引用類型, 并且返回值類型也是引用時, 編譯器就要求必須填寫完整的生命周期.
fn error_example_that_compile_not_fill_lifetime() {        // 問題代碼, 需注釋掉才能運行
    fn print(status: &str, msg: &str) -> &str {          // 編譯器翻譯成 fn print<'a, 'b>(status: &'a str, msg: &'b str) -> &str {}
        if status == "200" {
            msg                                            // msg == &'b str        與   指定的 &str 不一致
        } else {
            status                                         // status == &'a str     與   指定的 &str 不一致
        }
    }

    print("200", "ok!");
    // error output: missing lifetime specifier, expected lifetime parameter.
    // 備注: 這個例子根本就不是比較生命周期的長短問題,
    //       而是實際返回值與指定返回值生命周期不一致的問題.
    // 推理:
    //       1. 已知編譯器會自動補充生命周期:
    //          fn print<'a, 'b>(status: &'a str, msg: &'b str) -> &str {}
    //          這是會報錯, 因為編譯器沒有給這個引用返回值類型補充生命周期,
    //          這是因為當返回值是一個引用時, 必須包含生命周期.
    //
    //       2. 如果手動生命生命周期:
    //          fn print<'a, 'b>(status: &'a str, msg: &'b str) -> &'a str {};
    //          這樣也會報錯, 這是因為邏輯代碼塊里面不僅僅是返回 'a 這個生命周期里面的 status 引用變量,
    //          也有可能會返回 'b 生命周期里面的 msg 引用變量, 因此與指定的返回值 -> &'a str 不相符.
    //
    //       3. 唯一的解決辦法是, 認為的鎖定引用的生命周期, 都在一個生命周期里面.
    //          fn print<'a>(status: &'a str, msg: &'a str) -> &'a str {};
    //          通過這種方式, 不論返回的是status還是msg都是屬于 'a 生命周期,
    //          這種表達方式滿足兩種情況, 1. 他們的生命周期長短一致, 2. 返回值的類型與指定值的類型一致.
}


fn fix_error_example_that_compile_not_fill_lifetime() {
    fn print<'a>(status: &'a str, msg: &'a str) -> &'a str {
        if status == "200" {
            println!("{}: {}", status, msg);
            msg
        } else {
            println!("{}: {}", status, msg);
            status
        }
    }

    print("200", "ok!");
    print("404", "page not found!");
}


fn main() {
    multi_reference_parameters_and_return_value_is_not_reference_type();
    only_one_reference_parameter_and_return_value_type_is_reference_too();
    lifetime_on_method();
    // error_example_that_compile_not_fill_lifetime();
    fix_error_example_that_compile_not_fill_lifetime();
}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容