Borrowed value must be valid for the lifetime 'wtf

Hi
I'm desperately trying to understand why this code is not working:

extern crate regex;
use regex::Regex;
fn kaputt<'wtf>() -> Result<&'wtf str, &'static str> {
    let mut data: Vec<u8> = vec![60, 104, 101, 108, 108, 111, 62, 119, 111, 114, 108, 100, 60, 47, 104, 101, 108, 108, 111, 62, ];

    let re = Regex::new(r"<hello>(?P<foo>.*)</hello>").unwrap();

    let foo = String::from_utf8_lossy(&data); 

    match re.captures(&foo.to_string()) {
        Some(m) => Ok(m.name("foo").map_or("", |m| m.as_str())),
        _ => Err("kaputt"),
    }
}

fn main() {
    let blah = kaputt();
    println!("{:?}", blah);
}

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:12:24
   |
12 |     match re.captures(&foo.to_string()) {
   |                        ^^^^^^^^^^^^^^^ does not live long enough
...
16 | }
   | - temporary value only lives until here
   |
note: borrowed value must be valid for the lifetime 'wtf as defined on the function body at 5:1...
  --> src/main.rs:5:1
   |
5  | / fn kaputt<'wtf>() -> Result<&'wtf str, &'static str> {
6  | |     let mut data: Vec<u8> = vec![60, 104, 101, 108, 108, 111, 62, 119, 111, 114, 108, 100, 60, 47, 104, 101, 108, 108, 111, 62, ];
7  | |
8  | |     let re = Regex::new(r"<hello>(?P<foo>.*)</hello>").unwrap();
...  |
15 | |     }
16 | | }
   | |_^

Explicit lifetimes do not extend life of variables. data and foo are destroyed at the end of kaputt. Returned reference (&str) can't point to destroyed variable.

You either need to keep the data outside of kaputt or return owned value. Rust Book's ownership chapter could be useful here.

Here's a code that works.

3 Likes

Do not try to use references as if they were pointers.

&str and String are both pointers to a string, but &str means "this does not need to be freed" and String means "this needs a call to free() when you're done with it" (you don't call that free(), but Rust does it for you).

So if you do &foo.to_string() you're malloc()-ing a new String, and then trying to return a value that says to never free() it, so Rust protests, because you're about to create a memory leak or use-after-free bug.

If a function allocates a new object, it has to return the object, rather than a reference to it. If function sometimes allocates, sometimes borrows, there's Cow wrapper that carries a flag that tracks whether it needs to be freed or not.

4 Likes

I guess unlearning C is the hard part in learning rust :wink:

Thank you both for this excellent explanation.

2 Likes

Someone please make unlearnc.exe and unlearnc++.exe :joy::upside_down_face: