rustborrow-checkerrust-polars

How to fix "cannot return value referencing local variable" error?


I have read some answers to similar questions and still couldn't find an answer that fits my situation. I understand the problem here (I practically return dangling references) but because I use an external enum (polars_core::datatypes::any_value::AnyValue) I don't know how to handle this problem. here is a simplified version of my code:

fn foo<'a>(value1: &'a str, value2: &'a str) -> HashMap<String, AnyValue<'a>> {
    let mut result = HashMap::new();
    result.insert("key1".to_string(), AnyValue::String(value1));
    result.insert("key2".to_string(), AnyValue::String(value2));

    result
}

fn bar() -> HashMap<String, AnyValue<'static>> {
    let v1 = get_value();
    let v2 = get_value();

    foo(&v1, &v2)
}

fn get_value() -> String {
    "value".to_string()
}

The get_value() function is an external code so I can't change it.

the error I get:

error[E0515]: cannot return value referencing local variable `v2`
   --> src\main.rs:517:5
    |
517 |     foo(&v1, &v2)
    |     ^^^^^^^^^---^
    |     |        |
    |     |        `v2` is borrowed here
    |     returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `v1`
   --> src\main.rs:517:5
    |
517 |     foo(&v1, &v2)
    |     ^^^^---^^^^^^
    |     |   |
    |     |   `v1` is borrowed here
    |     returns a value referencing data owned by the current function

Bonus question: can anyone simplify 'static for me?

Thanks a lot!


Solution

  • Your types, as they are, are not compatible. With the return type

    fn bar() -> HashMap<String, AnyValue<'static>> {
    

    what you are saying is that the HashMap only depends on values with the 'static lifetime. However with your function

    fn foo<'a>(value1: &'a str, value2: &'a str) -> HashMap<String, AnyValue<'a>> {
    

    you are very much returning a non-'static datastructure since the lifetime depends on the input string lifetime.

    fn bar() -> HashMap<String, AnyValue<'static>> {
        let v1 = get_value();
        let v2 = get_value();
    
        foo(&v1, &v2)
    }
    

    will for example allocate two Strings on-stack, and you're passing references to those strings to foo. When bar eventually returns, those Strings will be freed, and nothing may reference them. Your code as it stands would be passing the caller of bar addresses that have already been freed.

    Since your goal is to return a structure with a 'static lifetime, you need to create that off the bat, without any arbitrary 'a stuff getting in the way.

    Looking over the polars docs, there appears to be an AnyValue::StringOwned variant, which would be a good fit for what you want. Given that, I'd instead make this function:

    fn foo<'a>(value1: &'a str, value2: &'a str) -> HashMap<String, AnyValue<'static>> {
        let mut result = HashMap::new();
        result.insert("key1".to_string(), AnyValue::StringOwned(value1.into()));
        result.insert("key2".to_string(), AnyValue::StringOwned(value2.into()));
    
        result
    }
    

    If you want to avoid additional allocations, you could also adjust the argument types to be Strings instead of &str and then pass that to StringOwned.