9

我一直试图了解 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(),因此引用没有静态生命周期。程序正确打印short这是 Rust Playground 中的示例

如果我们参考Rustonomicon(我很欣赏这是一个正在进行中的文档),我们会被告知函数签名的含义如下:

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

表示功能:

对 a 的引用u32具有一定的生命周期,并承诺它可以产生对 a 的引用,该引用的生命周期str也一样长。

现在让我们转向min()我的示例中的签名:

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

这更受欢迎,因为:

  • 我们有两个输入引用。
  • 它们的所指对象在不同的​​范围内定义,这意味着它们在不同的生命周期a内有效(有效期更长)。

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

  1. 该函数接受两个引用,并承诺产生一个对 a 的引用,该引用可以与str的所指对象一样长。a b不知何故感觉不对,好像我们将引用返回到bfrom ,那么显然该引用在 inmin()的生命周期内无效。amain()

  2. 该函数接受两个引用并承诺产生一个对 a 的引用,该引用可以与str的两个引用中较短的一个一样长。a b这可能会奏效,因为ab在 的内部范围内都保持有效main()

  3. 完全不同的东西?

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

4

3 回答 3

4

它是 (2):返回的引用与较短的输入生命周期一样长。

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

诀窍是调用者缩短了两个引用之一的生命周期以匹配min()s 函数签名。如果您有参考&'x T,您可以将其转换为&'y T iff 'x outlifes '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));
    //                    ^^^^^^^^^^^^^^^^^^
}

这与所谓的子类型有关,您可以在这个出色的答案中阅读更多相关信息。

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

于 2017-03-15T11:16:28.190 回答
2

我要去(3)别的东西

使用您的函数签名:

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

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

'a不是借用对象的生命周期。它是编译器为此调用生成的新生命周期。 a并且b将被借用(或可能重新借用)调用所需的时间,并通过返回值的范围进行扩展(因为它引用相同的'a)。

一些例子:

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借用的生命周期,尽管当然两者都必须比每个调用生成的生命周期更长。

游乐场

于 2017-03-15T12:33:34.680 回答
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生命周期将在 之前结束c

于 2017-03-15T12:26:17.937 回答