为什么这不是一个悬空指针?

Dan*_*ner 5 rust

我正在研究 Rust 书,它有以下代码片段

fn first_word(s: &String) -> usize {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return i;
        }
    }

    s.len()
}
Run Code Online (Sandbox Code Playgroud)

上一章已经说明

  • Rust 编译器会阻止您在对象超出范围后保留对对象的引用(悬空引用),并且
  • 变量在被提及的最后一刻超出范围(给出的示例显示了在同一块中创建对同一对象的不可变和可变引用,确保在创建之后不提及不可变引用可变引用)。

对我来说,它看起来在行头bytes之后没有被引用for(大概是关联的代码bytes.iter().enumerate()在循环开始前只执行一次,而不是在每次循环迭代中),所以&item不应该被允许成为bytes. 但是我没有看到任何其他对象(“对象”是正确的 rust 术语吗?)它可以作为参考。

确实,这s仍然在范围内,但是,好吧......编译器甚至记得循环滚动时bytes和之间的连接吗?确实,即使我将函数更改为直接接受,编译器也会认为事情很糟糕:sforbytes

fn first_word(bytes: &[u8]) -> usize {
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return i+1;
        }
    }

    0
}
Run Code Online (Sandbox Code Playgroud)

这里除了循环头之后没有提到i或之外的任何变量,所以它似乎真的不能作为任何东西的引用!itemfor&item

我看了这个标题非常相似的问题。那里的评论和答案表明可能有一个生命周期参数保持bytes活跃,并提出了一种方法来询问编译器它认为类型/生命周期是什么。我还没有了解生命周期,所以我在黑暗中摸索了一下,但我尝试过:

fn first_word<'x_lifetime>(s: &String) -> usize {
    let bytes = s.as_bytes();
    let x_variable: &'x_lifetime () = bytes;

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return i+1;
        }
    }

    0
}
Run Code Online (Sandbox Code Playgroud)

但是,该错误虽然确实表明bytes&[u8],但我理解,似乎也暗示没有与bytes. 我这样说是因为错误包括“预期”部分中的生命周期信息,但不包括“发现”部分中的生命周期信息。这里是:

error[E0308]: mismatched types
 --> test.rs:3:39
  |
3 |     let x_variable: &'x_lifetime () = bytes;
  |                     ---------------   ^^^^^ expected `()`, found slice `[u8]`
  |                     |
  |                     expected due to this
  |
  = note: expected reference `&'x_lifetime ()`
             found reference `&[u8]`
Run Code Online (Sandbox Code Playgroud)

那么这里发生了什么?显然,我的推理的某些部分是错误的,但是什么?为什么不是item悬空引用?

Kev*_*eid 4

这是第一个函数的版本,其中包含所有生命周期和类型,并将for循环替换为等效while let循环。

\n
fn first_word<\'a>(s: &\'a String) -> usize {\n    let bytes: &\'a [u8] = s.as_bytes();\n\n    let mut iter: std::iter::Enumerate<std::slice::Iter<\'a, u8>>\n        = bytes.iter().enumerate();\n\n    while let Some(iter_item) = iter.next() {\n        let (i, &item): (usize, &\'a u8) = iter_item;\n        if item == b\' \' {\n            return i;\n        }\n    }\n\n    s.len()\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这里需要注意的事项:

\n
    \n
  • 生成的迭代器的类型bytes.iter().enumerate()有一个生命周期参数,该参数确保迭代器的寿命不会超过它[u8]迭代的时间。(请注意,&[u8] 可以消失 \xe2\x80\x94\xc2\xa0it 不是必需的。重要的是它的所指对象,即内的字节String,保持活动状态。因此,我们只需要考虑一个生命周期\'as和没有单独的生命周期,因为我们以不同方式引用的bytes内部只有一个字节片。String
  • \n
  • iter\xe2\x80\x94\xc2\xa0 与 \xe2\x80\x94 的隐式操作相对应的显式变量在循环的每次迭代中for使用。
  • \n
\n

我看到另一个误解,不完全是关于生命周期和范围的:

\n
\n

可能会有终生的争论持续存在bytes

\n
\n

一生永远不会让某些东西活着。生命周期永远不会影响程序的执行;它们永远不会影响某些东西被丢弃或释放的时间。某些类型的生命周期&\'a u8是编译时声明,该类型的值将在该生命周期内有效。更改程序中的生命周期只会更改编译器要证明(检查)的内容,而不更改程序的真实情况

\n