Lifetime insanity - Rust bug or my error?

Hello!

I'm having trouble with a lifetime issue. I simplified my code to the example below:

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    let result = do_stuff(1, &mut map);
    println!("result is \"{result}\"");
}

fn do_stuff(key: usize, map: &mut HashMap<usize, String>) -> &String {
    if let Some(item) = map.get(&key) {
        println!("!");
        //return item;
    }
    
    map.insert(0, "dummy".to_string());
    return map.get(&0).unwrap();
}

[rust playground]

If I uncomment the line "return item;" inside of the if let statement, then my code no longer compiles. The borrow checker decides that my borrow of map for the if let is live for the entire function.
As you can see, only the return statement triggers that behavior, while e.g. println! is fine.

This doesn't match my understanding of how "liveness" should work. I expected the borrow to be released at the end of the if block, regardless of whether there is a return inside the if-block or not.

Am I missing something here? Or is this a bug in Rust?

It is pretty rare that Rust's borrow checker has a bug (but it happens: There are a few known soundness bugs where it accepts unsound code and a few limitations that cause it to reject sound code.)

This is one of the latter bugs, specifically Problem case #3: conditional control flow across functions. There are workarounds included in the link.

3 Likes

For the particular example, you probably want to use the entry API.

    map.entry(key)
        .and_modify(|_item| println!("!"))
        .or_insert_with(|| "dummy".to_string())
2 Likes

Thanks for the suggestion.
I know about the entry API, but it won't work for me at all - remember, the code above is greatly simplified.

What I'm actually doing is extracting type information from debug data. If the type is a struct or union, then it has multiple sub-entries for the struct members, which all also need to be extracted, so the function needs to recurse. If the struct is already present in the map, then I know all of the members are present as well and could return early.

Of course non if this was relevant for my question, so I left it all out.

1 Like