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

Clarify the meaning of binding two references to differently scoped referents to the same lifetime in a function signature

我一直在努力了解 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() 只是 return 对两个 引用的 字符串中较短的一个的引用。 main() 传入两个字符串引用,它们的引用对象定义在不同的范围内。我使用了 String::from() 以便引用没有静态生命周期。该程序正确打印 shortHere is the example in the Rust Playground.

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

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

表示函数:

takes a reference to a u32 with some lifetime, and promises that it can produce a reference to a str that can live just as long.

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

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

这更受喜爱,因为:

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

  1. 该函数接受两个引用并承诺生成对 str 的引用,该引用可以与 a [=63] 的引用一样长=]and b? 感觉有点不对,就好像我们 return 从 min() 引用 b 一样,那么很明显引用在 main().

  2. 中的 a 的生命周期内无效
  3. 该函数接受两个引用并承诺生成对 str 的引用,该引用的寿命与 [=20= 的两个引用对象中较短的那个一样长] and b? 这可行,因为 ab 的两个引用在 main().

  4. 完全是别的东西?

总而言之,我不明白将 min() 的两个输入引用的生命周期绑定到相同的生命周期是什么意思,因为它们的引用对象在调用者的不同范围内定义。

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

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

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

这与称为 子类型 的东西有关,您可以在 .

中阅读更多相关信息

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

除了@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 生命周期将在 [=13 之前结束=].

我要去 (3) 别的地方!

使用你的函数签名:

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

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

'a 不是被借用对象的生命周期。它是编译器只为这次调用生成的新生命周期。 ab 将被借用(或可能重新借用)只要调用需要,扩展 return 值的范围(因为它引用相同的 '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 的生命周期不同,当然两者都必须比每次调用的生成生命周期。

(Playground)