当输入非常清楚时,为什么借位检查器需要使用生命周期标签进行输出?

时间:2019-01-09 04:28:20

标签: rust lifetime borrow-checker borrowing

为什么下面的代码中的借用检查器对生存期感到困惑

fn main() {
    let ss = "abc"; // lets say 'a scope
    let tt = "def"; // lets say 'b scope
    let result = func(ss, tt);
}    

fn func(s: &str, t: &str) -> &str {
    t
}
| fn func(s: &str, t: &str) -> &str {
|                              ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s` or `t`

为什么这段代码中的内容无关紧要?我是否缺少一些非常重要的优势案例?

但是当我用生命标签注释它们时,它会起作用。

fn func<'a>(s: &'a str, t: &'a str) -> &'a str {
    t
}

我读到每个变量绑定(let)都创建一个隐式作用域,那么2个输入变量为何具有相同的作用域。纠正我,如果我很疲倦。在函数调用“ func”堆栈中,先按“ s”,然后按“ t”,因此,“ s”和“ t”具有不同的生存期。首先删除“ t”,然后删除“ s”。

2 个答案:

答案 0 :(得分:6)

您尚未告诉编译器返回值是否可以从st,两者或两者都不借用:

fn from_s<'a, 'b>(s: &'a str, t: &'b str) -> &'a str {
    // can be abbreviated: fn from_s<'a>(s: &'a str, t: &str) -> &'a str
    s
}

fn from_t<'a, 'b>(s: &'a str, t: &'b str) -> &'b str {
    // can be abbreviated: fn from_t<'a>(s: &str, t: &'a str) -> &'a str
    t
}

fn from_both<'a>(s: &'a str, t: &'a str) -> &'a str {
    if s < t {
        s
    } else {
        t
    }
}

fn from_neither<'a, 'b>(s: &'a str, t: &'b str) -> &'static str {
    // can be abbreviated: fn func(s: &str, t: &str) -> &'static str
    "foo"
}

如果您未编写'static,则编译器可以假定最后一个不是您想要的。但是您仍然需要在前三个之间消除歧义。

要了解差异为何如此重要,请考虑像这样的呼叫者

fn main() {
    let s = String::from("s");
    let r;
    {
        let t = String::from("t");
        r = from_s(&s, &t);
        // t goes out of scope
    }
    println!("{}", r);
}

如果编译器允许您调用from_t而不是from_s,则您将打印已经释放的字符串。

答案 1 :(得分:1)

如果我理解正确,那么问题是“为什么两个参数的寿命相同?”简短的答案是,生存期注释不是具体值,而是 bounds -它指出“此值的生存期不得超过此生存期。” / p>

当您按照有问题的方式编写代码时,fn func<'a>(s: &'a str, t: &'a str) -> &'a str的意思是:

  • 有一些寿命-我们将其命名为'a,每个呼叫站点上的寿命可以不同。
  • 自变量st的生存时间必须不少于 而不是'a(对于字符串文字,情况总是如此,因为它们是{{1 }},但对于强制{{1}的'static而言可能不成立)-也就是说,函数类型在参数类型上是 contra 变量(而生存期是类型的一部分) )。
  • 返回值必须存在的时间 ,然后&String-函数类型在返回类型上 co <​​/ strong>。

(有关方差的更多信息,请参见Rustonomicon

简化,这意味着两个参数都必须超过返回值。这并不总是您想要的-考虑以下情况(请注意,我现在返回&str,这样初始化顺序就不会改变):

'a

playground

此代码虽然在逻辑上是正确的,但不会编译,因为生命周期注释与逻辑不一致:第二个参数s绝不与返回值连接,但它限制了返回值根据功能注释,其寿命。但是当我们将功能更改为以下内容时:

fn main() {
    let ss = "abc";
    let mut result = "";
    {
        let tt = "def".to_string();
        result = func(ss, &tt);
    }
    println!("{}", result);
}    

fn func<'a>(s: &'a str, t: &'a str) -> &'a str {
    s
}

...它会编译并返回所需的结果(尽管有一些警告),因为生命周期tfn func<'a, 'b>(s: &'a str, t: &'b str) -> &'a str { s } 无关,实际上,它可以完全删除-终生淘汰会很好地工作。