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();
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容