澄清将对不同范围的引用绑定的两个引用绑定到函数签名中的相同生命周期的含义

时间:2017-03-15 10:53:25

标签: rust lifetime borrow-checker memory-safety

我一直试图了解Rust的借款和所有权模式。

假设我们有以下代码:

fn main() {
    let a = String::from("short");
    {
        let b = String::from("a long long long string");
        println!("{}", min(&a, &b));
    }
}

fn min<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.len() < b.len() {
        return a;
    } else {
        return b;
    }
}

min()只返回对两个引用的字符串中较短者的引用。 main()传入两个字符串引用,其引用在不同的范围内定义。我使用了String::from(),因此引用没有静态生命周期。程序正确打印shortHere is the example in the Rust Playground

如果我们引用Rustonomicon(我欣赏的是正在进行中的工作文档),我们会被告知函数签名的含义如下:

fn as_str<'a>(data: &'a u32) -> &'a str

表示功能:

  

引用具有生命周期的u32,并承诺它可以生成对str的引用,该引用可以存活一段时间。

现在让我们转到我的例子min()的签名:

fn min<'a>(a: &'a str, b: &'a str) -> &'a str

这更受欢迎,因为:

  • 我们有两个输入参考。
  • 他们的指示物在不同的范围内定义,意味着它们对不同的生命期有效(a有效期更长)。

使用与上述引用语句类似的措辞,min()的函数签名是什么意思?

  1. 该函数接受两个引用,并承诺生成对str的引用,该引用只要a {的引用就可以存在{1}}?以某种方式感觉不对,好像我们从b返回对b的引用,然后很明显该引用在min()的生命周期内无效a

  2. 该函数接受两个引用,并承诺生成对main()的引用,该引用只要str 和<的两个指示对象中的较短者即可生存/ em> a这可能有效,因为ba的指示对象在b的内部范围内仍然有效。

    < / LI>
  3. 还有其他什么?

  4. 总而言之,我不明白将main()的两个输入引用的生命周期绑定到同一生命周期,当它们的引用在被调用者的不同范围内定义时,意味着什么。

3 个答案:

答案 0 :(得分:4)

它是(2):只要输入的生命周期较短,返回的引用就会存在。

但是,从功能的角度来看,两个输入生命周期实际上都是相同的(都是'a)。因此,a的变量main()显然比b更长,这是如何工作的?

诀窍是调用者缩短了两个引用之一的生命周期,以匹配min()的函数签名。如果您有参考&'x T,则可以将其转换为&'y T iff 'x超过'y(也写为:'x: 'y) 。这具有直观意义(我们可以缩短参考的生命周期而不会产生不良后果)。编译器自动执行此转换。所以想象编译器会将你的main()变成:

let a = String::from("short");
{
    let b = String::from("a long long long string");

    // NOTE: this syntax is not valid Rust! 
    let a_ref: &'a_in_main str = &a;
    let b_ref: &'b_in_main str = &b;
    println!("{}", min(&a as &'b_in_main str, &b));
    //                    ^^^^^^^^^^^^^^^^^^
}

这与名为子类型的内容有关,您可以在this excellent answer中详细了解这一点。

总结:调用者缩短一个生命周期以匹配函数签名,以便函数可以假设两个引用具有相同的生命周期。

答案 1 :(得分:2)

我要去(3)别的东西

使用您的功能签名:

fn min<'a>(a: &'a str, b: &'a str) -> &'a str { ...}

// ...
min(&a, &b)

'a 借来的对象的生命周期。它是由编译器为此调用生成的新生命周期。只要呼叫需要,ab将被借用(或可能重新借用),并由返回值的范围扩展(因为它引用相同的'a)。 / p>

一些例子:

let mut a = String::from("short");
{
    let mut b = String::from("a long long long string");
    // a and b borrowed for the duration of the println!()
    println!("{}", min(&a, &b));
    // a and b borrowed for the duration of the expression, but not
    // later (since l is not a reference)
    let l = min(&a, &b).len();

    {
        // borrowed for s's scope
        let s = min(&a, &b);
        // Invalid: b is borrowed until s goes out of scope
        // b += "...";
    }
    b += "...";  // Ok: b is no longer borrowed.
    // Borrow a and b again to print:
    println!("{}", min(&a, &b));
}

正如您所看到的,任何单个调用的'a都与借用的实际ab的生命周期不同,但当然两者都必须比生成的生命周期更长每次通话。

Playground

答案 2 :(得分:1)

除了@Lukas在答案中提到的内容之外,您还可以将函数的签名读取为 - 返回的引用有效,直到两个传递的引用都有效,即它之间的连接(又称AND)参数寿命。

还有更多内容。以下是两个代码示例:

    let a = String::from("short");
    {
        let c: &str;
        let b = String::from("a long long long string");
        c = min(&a, &b);

    } 

let a = String::from("short");
    {
        let b = String::from("a long long long string");
        let c: &str;
        c = min(&a, &b);

    }

第一个不起作用(第二个不起作用)。似乎bc的生命周期与相同范围内的生命周期相同,但范围中的排序也很重要,因为第一种情况b生命周期将在{{1}之前结束}}