为什么Rust忽略了对&str的生命周期检查?

8
fn main() {
    let strA = "a";
    let result;

    {
        let strB = "abc";
        result = longest(strA, strB); // Will return strB
    }

    println!("The longest string is {}", result); // result now point to strB!!
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

Rust官方文档中了解到:

'a将得到一个具体生命周期,该生命周期等于xy的生命周期中较小的一个

那么为什么strB现在可以在其作用域之外被访问呢?


1
这个回答解决了你的问题吗?为什么通用生命周期不符合嵌套作用域更小的生命周期? - Steve Z
3个回答

8

这是因为所有字符串字面值都具有'static生命周期。来自Rust书籍

One special lifetime we need to discuss is 'static, which means that this reference can live for the entire duration of the program. All string literals have the 'static lifetime, which we can annotate as follows:

let s: &'static str = "I have a static lifetime.";

The text of this string is stored directly in the program’s binary, which is always available. Therefore, the lifetime of all string literals is 'static


3

修正后的示例:

fn main() {
    let strA = "a".to_string();
    let result;

    {
        let strB = "abc".to_string();
        result = longest(&strA, &strB); // Will return strB
    }

    println!("The longest string is {}", result); // compile error
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

现在会像预期的那样产生编译器错误:

error[E0597]: `strB` does not live long enough
  --> src/main.rs:7:33
   |
7  |         result = longest(&strA, &strB); // Will return strB
   |                                 ^^^^^ borrowed value does not live long enough
8  |     }
   |     - `strB` dropped here while still borrowed
9  | 
10 |     println!("The longest string is {}", result); // result now point to strB!!
   |                                          ------ borrow later used here

Playground

在你的初始示例中,Rust 并没有“忽略”字符串变量的生命周期。当你将一个变量设置为字符串字面量时,该字面量被硬编码到可执行二进制文件中,并获得 'static 生命周期,这意味着它在程序的整个运行期间都是有效的。如果我们给你的初始示例添加显式类型注释,就可以清楚地看到为什么它能够通过编译并正常工作:

fn main() {
    let strA: &'static str = "a";
    let result;

    {
        let strB: &'static str = "abc";
        result = longest(&strA, &strB); // returns 'static str
    }

    println!("The longest string is {}", result); // prints result
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

然而当我们在字符串字面量上调用to_string()时,我们创建了一个拥有所有权且堆分配的String,它的生命周期是非静态的,并被限定在所在的块内,因此对代码进行更改会使程序不再按预期编译。


1
生命周期 'a 指的是字符串缓冲区 str 的生命周期,而不是对该缓冲区的引用。因此,&str strB 的生命周期在块内。然而,"abc" 是一个常量字符串。这意味着 str 缓冲区的存储具有 'static 生命周期,这意味着它保证比任何其他生命周期更长。因此,即使 strB 不再引用它,result 引用该缓冲区仍然有效。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接