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

时间:2015-07-06 05:29:50

标签: rust lifetime

考虑下面的代码(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

当然这是胡说八道!除其他事项外,该行与本身冲突。我在这个错误中可以看到的唯一线索是循环正在展开的事实。但是,在循环中所做的所有借用都不应该在每次迭代结束时到期吗?

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

1 个答案:

答案 0 :(得分:4)

Rust中不可能引用同一结构中的某些内容。

想一想:

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

'a的目的是什么? text字段的生命周期(顺便说一句,围绕Box的{​​{1}}包裹是完全多余的。因此,在它已经存在之前,你不能表达它。

如果允许这样的 ,你就会遇到这样的问题:

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实例具有相同的生命周期。这意味着推断的生命周期必须大于循环的生命周期,因此循环的每次迭代确实使可变引用保持活着,因此该行实际上与其自身(或者更确切地说,先前的迭代)冲突办法。循环没有展开 - 它只是循环的借用确实还活着。