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;
}
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 aLines
instance, which is anIterator<IoResult<String>>
.a_line
is anIoResult
<String> = Result<String, IoError>
, and so.unwrap
returns aString
.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 theString
exists avoiding the problem of dangling references and use-after-free in C++ (for more aboutString
vs.&str
: this answer & the strings guide)..captures
takes a&str
with some lifetime (the't
) and tries to return aCaptures
that lasts that long. In this case, the lifetime of the&str
is the lifetime of theString
out ofa_line
, and soc
is aCaptures
storing data that is only valid this long..at
returns a&str
with the't
lifetime of the data that theCaptures
is storing, that is, the returned&str
is only guaranteed to last as long as the original&str
fed intocaptures
(which can't be longer than how long the originalString
exists, since that's managing the text in memory)Hence,
c1
only lasts for as long as theString
froma_line
, and thatString
is scoped inside the loop, that is, each time you step through the loop, you get a newString
which is deallocated at the end. If the compiler let it escape by allowing it to be placed invect1
, the code would be prone to use-after-free/dangling reference memory safety bugs as theString
each&str
points into is freed at the end of each iteration (e.g. all the&str
s invect1
on thereturn vect1
line would be pointing to garbage).To fix this, you need to cut the memory dependency: currently the
&str
s are not in control of their own memory, and thus are dependent on the "parent"String
s being placed correctly. Instead, you could give the vector contents control of its own destiny (well, memory), by making them fully-fledgedString
s, e.g.vect1.push(c1.to_string())
. This will makevect1
aVec<String>
, and then there is no longer a connection between those values and thea_line
value inside the loop. That variable can then be freed/mangled/munged as much as it likes without affecting the contents ofvect1
.