Search code examples
rustreferencehashmap

Result of HashMap .get() call references local variable used for lookup


I'm attempting to perform multiple lookups into a HashMap. In the program's main loop, my method works just fine. However, once I try to move the logic into a function to hide some messy implementation details (case sensitivity of the HashMap keys), I get an error from the compiler stating: returns a value referencing data owned by the current function, aimed at (x86_instruction, x86_64_instruction). The code as in the program's main loop:

let raised_word = word.to_uppercase();
let x86_instruction =
    names_to_instructions.get(&(Arch::X86, &*word)).map_or_else(
        || {
            names_to_instructions
                .get(&(Arch::X86, &raised_word))
        },
        |instr| Some(instr),
    );
let x86_64_instruction =
    names_to_instructions.get(&(Arch::X86_64, &*word)).map_or_else(
        || {
            names_to_instructions
                .get(&(Arch::X86_64, &raised_word))
        },
        |instr| Some(instr),
   );

Attempting to move into a function:

pub fn search_for_instr<'a>(
    word: &'a str, 
    map: &'a NameToInstructionMap<'a>
) -> (Option<&'a&'a Instruction>, Option<&'a&'a Instruction>) {
    let raised_word = word.to_uppercase();

    let x86_instruction =
    map.get(&(Arch::X86, &*word)).map_or_else(
        || {
            map
                .get(&(Arch::X86, &raised_word))
        },
        |instr| Some(instr),
    );
    let x86_64_instruction =
    map.get(&(Arch::X86_64, &*word)).map_or_else(
        || {
            map
                .get(&(Arch::X86_64, &raised_word))
        },
        |instr| Some(instr),
   );

    (x86_instruction, x86_64_instruction)
}

I don't understand why a result of a HashMap lookup is referencing data used for its lookup via .get(). There are likely ways to get around this issue with some carefully placed .clone()'s, but I'd prefer a cleaner solution if possible. I also realize that I could pass in raised_word as a String argument rather than create it as a local variable, but this feels rather leaky to me abstraction-wise.


Solution

  • This seems like Issue 80389 "Lifetime of input (key) to HashMap::get() gets entanged w/ that of its output (value)": the signature of HashMap::get is

    fn get<Q>(&self, k: &Q) -> Option<&V>
    

    so &V takes the lifetime of &self and &Q, aka it is tied to the lifetime of the key, even though the key is only needed during retrieval and irrelevant thereafter. So sadly I don't think there's a fix, except cloning the result of get.

    Do note that your code is way more complicated than necessary, the map_ of map_or_else is not useful, and neither is the double reference in the result. Also the lifetime of the map and that of its values should very much not be the same.