来自展开循环的可变借用冲突

Mutable borrow conflict from unrolled loop

考虑以下代码 (Playpen),它旨在简单地解析来自 stdin 的输入并将每一行和对该行的引用放在一个结构中:

use std::io;
use std::io::BufRead;

struct Line<'a> {
    text: Box<String>,
    column: &'a str,
}

fn main() {
    let column = 1;
    let stdin = io::stdin();
    let mut lines: Vec<Line> = Vec::new();

    for line_res in stdin.lock().lines() {
        lines.push(Line {
            text: Box::new(line_res.unwrap()),
            column: "",
        });

        let line = lines.last_mut().unwrap();
        line.column = line.text.split_whitespace().nth(column)
                      .unwrap_or("");
    }
}

Line::column应该是指Line::text。请注意,我最初将 column 设置为 ""(稍后修改),因为我不知道在创建时引用 text 元素的方法。

不幸的是,上面的代码无法编译,吐出以下非常迟钝的错误消息:

<anon>:15:3: 15:8 error: cannot borrow `lines` as mutable more than once at a time
<anon>:15       lines.push(Line {
                ^~~~~
note: in expansion of for loop expansion
<anon>:14:2: 23:3 note: expansion site
<anon>:20:14: 20:19 note: previous borrow of `lines` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `lines` until the borrow ends
<anon>:20       let line = lines.last_mut().unwrap();
                           ^~~~~
note: in expansion of for loop expansion
<anon>:14:2: 23:3 note: expansion site
<anon>:24:2: 24:2 note: previous borrow ends here
<anon>:9 fn main() {
...
<anon>:24 }
          ^
<anon>:20:14: 20:19 error: cannot borrow `lines` as mutable more than once at a time
<anon>:20       let line = lines.last_mut().unwrap();
                           ^~~~~
note: in expansion of for loop expansion
<anon>:14:2: 23:3 note: expansion site
<anon>:20:14: 20:19 note: previous borrow of `lines` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `lines` until the borrow ends
<anon>:20       let line = lines.last_mut().unwrap();
                           ^~~~~
note: in expansion of for loop expansion
<anon>:14:2: 23:3 note: expansion site
<anon>:24:2: 24:2 note: previous borrow ends here
<anon>:9 fn main() {
...
<anon>:24 }
          ^
<anon>:20:14: 20:19 error: cannot borrow `lines` as mutable more than once at a time
<anon>:20       let line = lines.last_mut().unwrap();
                           ^~~~~
note: in expansion of for loop expansion
<anon>:14:2: 23:3 note: expansion site
<anon>:20:14: 20:19 note: previous borrow of `lines` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `lines` until the borrow ends
<anon>:20       let line = lines.last_mut().unwrap();
                           ^~~~~
note: in expansion of for loop expansion
<anon>:14:2: 23:3 note: expansion site
<anon>:24:2: 24:2 note: previous borrow ends here
<anon>:9 fn main() {
...
<anon>:24 }
          ^
error: aborting due to 3 previous errors

这绝对是胡说八道!该行与自身 冲突 等。我在这个错误中看到的唯一线索是循环正在展开。但是,循环中的所有借用不应该在每次迭代结束时到期吗?

以上代码的实际语义问题是什么,修复方法是什么?

在 Rust 中不可能引用同一结构中的某些东西。

想想看:

struct Line<'a> {
    text: Box<String>,
    column: &'a str,
}

'a 的目的是什么? text 字段的生命周期(顺便说一下,Box 环绕 String 是完全多余的)。因此,您无法表达类型,直到它已经存在。

如果允许这样的引用,你会运行遇到这样的问题:

let mut line = Line { text: "foo".to_owned(), column: "" };
line.column = &self.text;
line.text = "bar".to_owned();
// Uh oh, column is now invalid, pointing to freed memory

当两个值存储在一起时,没有办法解决这个问题;它们必须分开存放。最可能适合您的情况的解决方法是存储索引,例如开始和结束索引为 (usize, usize).

现在:为什么会出现这些特定错误?归结为 'a 被推断为什么;您的线向量是 Vec<Lines<'x>> 一个生命周期 'x每个 Lines 实例具有相同的生命周期 。这意味着推断的生命周期必须大于循环的生命周期,因此循环的每次迭代确实使可变引用保持活动状态,因此该行实际上与自身(或者更确切地说,先前的迭代)冲突方法。循环没有展开——只是循环中的借用确实还活着。