Prolonging a 'for' variable's life

2020-04-14 09:29发布

when I compile the below code in Rust 0.12.0, I get the following error:

error: borrowed value does not live long enough
let _ = match re.captures(a_line.unwrap().as_slice()) {

How do I prolong a_line's lifetime so that c1 can be pushed onto vect1?

let vect = process_file(filename_ref);
...
fn process_file(filename: &str) -> Vec<&str> {

    let re = regex!(r"^(\w+)\t(\w+)\t(\w+)\t(\w+)\n$");
    let mut vect1 = Vec::new();

    let filepath = Path::new(filename);
    let _ = match File::open(&filepath) {
        Ok(f) => {
            let mut filebuffer = BufferedReader::new(f);
            for a_line in filebuffer.lines() {
                let _ = match re.captures(a_line.unwrap().as_slice()) {
                    Some(c) =>  {
                        let c1 = c.at(1);
                        vect1.push(c1);
                        ...
                    },
                   ...
               };
           } // end for
       },
       Err(e) => println!("Error: {}", e)
   };

   return vect1;

}

标签: rust
1条回答
相关推荐>>
2楼-- · 2020-04-14 09:49

The short answer is, no, you can't just 'extend' the lifetime of a variable like this.

This is a game of connect the types:

  • .lines returns a Lines instance, which is an Iterator<IoResult<String>>.
  • a_line is an IoResult<String> = Result<String, IoError>, and so .unwrap returns a String
  • .as_slice gives a non-owning view of the string's data which is, via the 'a lifetime on the references, statically restricted to only be usable while the String exists avoiding the problem of dangling references and use-after-free in C++ (for more about String vs. &str: this answer & the strings guide).
  • .captures takes a &str with some lifetime (the 't) and tries to return a Captures that lasts that long. In this case, the lifetime of the &str is the lifetime of the String out of a_line, and so c is a Captures storing data that is only valid this long.
  • .at returns a &str with the 't lifetime of the data that the Captures is storing, that is, the returned &str is only guaranteed to last as long as the original &str fed into captures (which can't be longer than how long the original String exists, since that's managing the text in memory)

Hence, c1 only lasts for as long as the String from a_line, and that String is scoped inside the loop, that is, each time you step through the loop, you get a new String which is deallocated at the end. If the compiler let it escape by allowing it to be placed in vect1, the code would be prone to use-after-free/dangling reference memory safety bugs as the String each &str points into is freed at the end of each iteration (e.g. all the &strs in vect1 on the return vect1 line would be pointing to garbage).

To fix this, you need to cut the memory dependency: currently the &strs are not in control of their own memory, and thus are dependent on the "parent" Strings being placed correctly. Instead, you could give the vector contents control of its own destiny (well, memory), by making them fully-fledged Strings, e.g. vect1.push(c1.to_string()). This will make vect1 a Vec<String>, and then there is no longer a connection between those values and the a_line value inside the loop. That variable can then be freed/mangled/munged as much as it likes without affecting the contents of vect1.

查看更多
登录 后发表回答