Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the variable not live long enough?

Consider this function that should return the file extension of a given Path.

pub fn get_extension<'a>(path: &'a Path) -> Option<&'a str> {
    let path_str = path.as_str().unwrap();
    let ext_pos = regex!(".[a-z0-9]+$").find(path_str);

    match ext_pos {
        Some((start, _)) => {
            return Some(path_str.as_slice().slice_from(start))
        },
        None => return None
    }
}

The error message is as follows:

`path_str` does not live long enough

enter image description here

The error message is pretty clear and it's a shame I can't work it out on my own. I understand it in theory but there are still a couple of blurred things for me.

I understand that the compiler wants to tell me that path_str does not live long enough to be valid as the return value with is marked with lifetime 'a.

But this is where it stops for me:

  • I understand that the reference to path (the input parameter) should life exactly as long as the reference to the str that is wrapped in the Option (the output parameter)

  • since we return Some(path_str.as_slice().slice_from(start)) I assume that in practice that means that path_str needs to live as long as path.

What I don't understand is why exactly does path_str not live long enough and how could I fix this? What makes it die to soon?

UPDATE

As pointed out in the comments and also on IRC removing the superflous as_slice() makes the code compile. Does anyone know why that is? It was also pointed out that there exists a method to get the extension directly. But yep, I'm actually more interested in learning the story behind the problem though.

like image 963
Christoph Avatar asked Jul 02 '14 22:07

Christoph


People also ask

How do you fix borrowed value does not live long enough?

These word references cannot outlive the if let block, which means you cannot use them as keys in the hash map. The easiest fix is to have words_matches own its keys. Without borrows there are no lifetimes, and thus no lifetime issues.

What is static lifetime rust?

A static is never "inlined" at the usage site, and all references to it refer to the same memory location. Static items have the static lifetime, which outlives all other lifetimes in a Rust program. Static items may be placed in read-only memory if the type is not interior mutable.


1 Answers

This isn't a bug. The "problem" here is as_slice's definition. It takes a reference to its arguments, and returns a &str with the same lifetime as the reference, it can't introspect into the internal lifetimes of whatever type it is being called on. That is, path_str.as_slice() returns a &str that lasts for as long as path_str, not as long as the data path_str points at (the original Path).

In other words, there's two lifetimes here. I'll use a hypothetical block-lifetime annotation syntax on the example from @Arjan's filed bug (this answer is based of my response there).

fn test<'a>(s: &'a String) -> &'a str {
    'b: {
        let slice: &'a str = s.as_slice();
        slice.as_slice()
    }
}

For the second as_slice call we have self: &'b &'a str, and thus it returns &'b str, which is too short: 'b is just local to test.


As you discovered, the fix now is just removing the extraneous as_slice call. However, with dynamically sized types (DST), we will be able to write impl StrSlice for str, and then slice.as_slice() will be returning a &'a str, since there won't be an extra layer of references (that is, self: &'a str).

like image 60
huon Avatar answered Oct 08 '22 05:10

huon