ZhangHanDong / tao-of-rust-codes

《Rust编程之道》随书源码
https://ruststudy.github.io/tao_of_rust_docs/tao_of_rust/
MIT License
1.18k stars 170 forks source link

第五章 多个生命周期参数 #330

Closed niconical closed 1 year ago

niconical commented 2 years ago

页码与行数

fn the_longest<'a,'b:'a>(s1: &'a str, s2: &'b str) -> &'a str {
    if s1.len() > s2.len() { s1 } else { s2 }
}

main函数(调用the_longest)代码:

// 节选自代码清单5-32
let s1 = String::from("Rust");
let s1_r = &s1;
{
    let s2 = String::from("C");
    let res = the_longest(s1_r, &s2);
    println!("{} is the longest", res); // Rust is the longest
}

代码清单5-34以及下方解释为什么在多生命周期参数下调用the_longest的第一个参数s1_r的生命周期'a长于&s2生命周期'b,但是the_longest的lifetime subtype却是'b:'a,即'b outlive ‘a,但是使用'a:'b以下代码也可以编译通过:

// change lifetime subtype
fn the_longest<'a:'b,'b>(s1: &'a str, s2: &'b str) -> &'b str {
    if s1.len() > s2.len() { s1 } else { s2 }
}

所以”但为什么现在'b:'a表示’b的存活时间长于'a的呢?(p140)“这个问题似乎在隐含只可以使用'b:'a?

ZhangHanDong commented 2 years ago

@niconical 说明你还没有理解生命周期参数的本质。

“为什么现在'b:'a表示’b的存活时间长于'a的呢?”

因为这就是编译器规则,语法就是这么规定的。生命周期参数语法就是给开发者来指定的,在你确定输入引用和输出引用的合法关系之后,如果需要指定'b:'a,那就这么指定。不是让你滥用的。并不是说你随便指定个''a : 'b ',实际引用的生命周期就是这样的。 生命周期参数是开发者来辅助编译器来判断引用合法性,而不是让你修改引用的实际生命周期。

看你修改的这段代码。

fn the_longest<'a:'b,'b>(s1: &'a str, s2: &'b str) -> &'b str {
    if s1.len() > s2.len() { s1 } else { s2 }
}

fn main() {
    let s1 = String::from("Rust"); // 'a 的生命周期参数实例假如是 '1   
    let s1_r = &s1;                       
    {
        let s2 = String::from("C");  // 'b 的生命周期参数实例假如是 '2
        let res = the_longest(s1_r, &s2);   // 返回值res ( 'b)  的生命周期参数实例假如是 '3 ,
        println!("{} is the longest", res); // Rust is the longest
    }
}

注意:编译器检查只判断 输入位置和输出位置(返回值)的引用生命周期长短关系!!!

所以,现在满足 '1 的生命周期比 '3 长 , '1 : '3 ,符合the_longest 函数定义的生命周期参数约定 'a: 'b

编译器检测main里面实际调用函数的代码,符合你函数定义时生命周期参数约定,就会编译通过,表示你这段代码执行不会出现悬垂指针。

再回头看书里的示例:


fn the_longest<'a,'b:'a>(s1: &'a str, s2: &'b str) -> &'a str {
    if s1.len() > s2.len() { s1 } else { s2 }
}

fn main() {
    let s1 = String::from("Rust"); // 'a 的生命周期参数实例假如是 '1   
    let s1_r = &s1;                       
    {
        let s2 = String::from("C");  // 'b 的生命周期参数实例假如是 '2
        let res = the_longest(s1_r, &s2);   // 返回值 res ('a) 的 的生命周期参数实例假如是 '3
        println!("{} is the longest", res); // Rust is the longest
    }
}

注意:编译器检查只判断 输入位置和输出位置(返回值)的引用生命周期长短关系!!!

现在同样满足 满足 '2 的生命周期比 '3 长 , '2 : '3 ,符合the_longest 函数定义的生命周期参数约定 'b: 'a

当然都可以通过编译了。

另外: 建议你把书里内容完整看一遍,自己思考思考,再来提问题。也许等你思考完就不会有问题了。