Hassle-free conflict of borrowing from an expanded loop

Consider the following code ( Playpen ), which is designed to easily parse input from stdin and place each line and link to that line in a structure:

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("");
    }
}

      

That is, it Line::column

must refer to Line::text

. Note that I first set column

to ""

(and changed it later) because I don't know how to reference the element text

at creation time.

Unfortunately, the above code does not compile, spitting out the following very dumb error message:

<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

      

Of course, this is nonsense! By the way, the line conflicts with itself. The only clue I can see in this error is the fact that the loop is unwrapping. However, shouldn't all the workpieces made in the loop expire at the end of each iteration?

What is the actual semantic issue with the above code and what is the fix?

+3


source to share


1 answer


In Rust it is not possible to have a reference to something in the same structure.

Think about it:

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

      

What is it 'a

? Lifetime of the field text

(by the way, wrap Box

around a String

is redundant). Thus, you cannot express a type until it already exists.



If such a link is allowed, you face problems like this:

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

      

There is no way around this while the two values ​​are stored together; they must be kept separately. The workaround that is most likely suitable for your case is to store the indexes, for example. start and end indices as (usize, usize)

.

Now: why are these specific errors? It boils down to what 'a

is displayed as; your vector of strings Vec<Lines<'x>>

for one lifetime 'x

: each instance Lines

has the same lifetime. This means that the estimated lifetime must be greater than the estimated cycle time, and therefore each iteration of the loop does indeed keep the volatile reference alive, and therefore the line actually conflicts with itself (or rather the previous iteration) in this way. The cycle does not unfold - its simply borrowings from the cycle are really alive.

+4


source







All Articles