Rust cannot return value referencing local variable on HashMap get

Viewed 2483

I have a code looks like this:

use std::collections::HashMap;

fn main() {
    let x = get_hash_map();
    println!("{:?}", x);
}

fn get_hash_map() -> Option<&'static Vec<i32>> {
    let mut hm = HashMap::new();
    let mut vec = Vec::new();
    vec.push(1);
    hm.insert("1".to_string(), vec);
    return hm.get("1");
}

But I got this error:

error[E0515]: cannot return value referencing local variable `hm`
  --> src/main.rs:13:12
   |
13 |     return hm.get("1");
   |            --^^^^^^^^^
   |            |
   |            returns a value referencing data owned by the current function
   |            `hm` is borrowed here

Here is the rust playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7605176aee2dd3ff77a0cfd04a89db55

Can anyone suggest the alternatives to fix this problem minimally? Thanks!

2 Answers
fn get_hash_map() -> Option<&'static Vec<i32>> {
    let mut hm = HashMap::new();
    let mut vec = Vec::new();
    vec.push(1);
    hm.insert("1".to_string(), vec);
    return hm.get("1");
}

This is invalid, because you have declared you are going to return an Option<&'static Vec<i32>>, but you are returning an Option<&'a Vec<i32>> where 'a is the current function lifetime. The HashMap will stop existing as soon as the function returns, freeing the vector, and the reference will then become dangling. This is the exact kind of situation the borrow checker is designed to avoid.

Just return the vector by value:

fn get_hash_map() -> Option<Vec<i32>> {
    let mut hm = HashMap::new();
    let mut vec = Vec::new();
    vec.push(1);
    hm.insert("1".to_string(), vec);
    return hm.remove("1");
}

remove moves the value out of the map, returning it as an Option<V>.

If you then need an Option<&Vec<i32>>, you can just use as_ref() on your Option<Vec<i32>> to get one. Always remember it will become invalid as soon as its value goes out of scope.

The HashMap hm is local to the scope of the get_hash_map() function and is dropped as soon as get_hash_map() returns. The value returned by hm.get("1") contains a reference to this HashMap, thus its lifetime is also tied to the scope of get_hash_map() which unfortunately is shorter than the ascribed 'static lifetime.

If you remove the 'static lifetime and replaced it by some 'a annotation on the function, you would get a similar error, since again there is no (sound) way to return borrowed data from a function that creates the owner of that data.

You can however create the map in the surrounding scope and pass it via a mutable reference to get_hash_map

use std::collections::HashMap;

fn main() {
    let mut hm = HashMap::new();
    let x = get_hash_map(&mut hm);
    println!("{:?}", x);
}

// note that both `hm` and the reference in the return type have the same 'a lifetime.
fn get_hash_map<'a>(hm: &'a mut HashMap<String, Vec<i32>>) -> Option<&'a Vec<i32>> {
    let mut vec = Vec::new();
    vec.push(1);
    hm.insert("1".to_string(), vec);
    hm.get("1");
}
Related