1

I'm trying to make a read-only map of environment variables.

fn os_env_hashmap() -> HashMap<&'static str, &'static str> {
    let mut map = HashMap::new();
    use std::env;
    for (key,val) in env::vars_os() {
        let k = key.to_str();
        if k.is_none() { continue } 
        let v = val.to_str();
        if v.is_none() { continue }
        k.unwrap();
        //map.insert( k.unwrap(), v.unwrap() );
    }
    return map;
}

Can't seem to uncomment the "insert" line near the bottom without compiler errors about key,val,k, and v being local.

I might be able to fix the compiler error by using String instead of str, but str seems perfect for a read-only result.

Feel free to suggest a more idiomatic way to do this.

trent
  • 25,033
  • 7
  • 51
  • 90
freeideas
  • 71
  • 5
  • 2
    You said it yourself: you should store owned types. It does not matter that the result is "read-only", the data has to live somewhere. You are trying to return `HashMap<&'static str, &'static str>`, but `key` and `val` only live for one iteration of the loop, so references to them can not be `'static`. – justinas May 31 '20 at 15:45
  • To add to @justinas comment: `'static` has absolutely nothing to do with “heap” v. “stack”. – mcarton May 31 '20 at 15:53
  • So far, I haven't been able to figure out how to use String to solve this problem either. Tried .to_owned() and, for example, "let k:String=". – freeideas May 31 '20 at 16:00

1 Answers1

7

This is unfortunately not straightforward using only the facilities of the Rust standard library.

If env::vars_os() returned an iterator over &'static OsStr instead of OsString, this would be trivial. Unfortunately, not all platforms allow creating an &OsStr to the contents of an environment variable. In particular, on Windows, the native encoding is UTF-16 but the encoding needed by OsStr is WTF-8. For this reason, there really is no OsStr anywhere you could take a reference to, until you create an OsString by iterating over env::vars_os().

The simplest thing, as the question comments mention, is to return owned Strings:

fn os_env_hashmap() -> HashMap<String, String> {
    let mut map = HashMap::new();
    use std::env;
    for (key, val) in env::vars_os() {
        // Use pattern bindings instead of testing .is_some() followed by .unwrap()
        if let (Ok(k), Ok(v)) = (key.into_string(), val.into_string()) {
            map.insert(k, v);
        }
    }
    return map;
}

The result is not "read-only", but it is not shared, so you cannot cause data races or other weird bugs by mutating it.

See also

trent
  • 25,033
  • 7
  • 51
  • 90
  • 1
    Not only did you answer with working code, you explained why it works, and -- amazingly -- you worded your explanation in a way that did not hurt my feelings! Fantastic. – freeideas Jun 02 '20 at 09:21