'How to invert a `HashMap<String, MyEnum>`? (Error: "`FromIterator` not implemented for [pair]")

I'm trying to invert a HashMap<String, MyEnum> in Rust, and getting an error:

Code:

use std::collections::HashMap;

// Directive added because of this answer, which eliminated a couple of errors:
// https://github.com/rust-lang/rust/issues/22756#issuecomment-75715762
#[derive(Debug, PartialEq, Eq, Hash)]
enum MyEnum {
    Value1,
    Value2,
}

static MYMAP: HashMap<String, MyEnum> = HashMap::from([
    (String::from("value1"), MyEnum::Value1),
    (String::from("value1"), MyEnum::Value1),
]);

static MYMAP_INVERTED: HashMap<MyEnum, String> = HashMap::from([
    MYMAP.iter().map(|(k, v)| (v, k)).collect()
]);

Error:

error[E0277]: a value of type `(_, _)` cannot be built from an iterator over elements of type `(&MyEnum, &String)`
    --> src/main.rs:15:39
     |
15   |     MYMAP.iter().map(|(k, v)| (v, k)).collect()
     |                                       ^^^^^^^ value of type `(_, _)` cannot be built from `std::iter::Iterator<Item=(&MyEnum, &String)>`
     |
     = help: the trait `FromIterator<(&MyEnum, &String)>` is not implemented for `(_, _)`
note: required by a bound in `collect`

For more information about this error, try `rustc --explain E0277`.

How would you implement FromIterator for a pair?



Solution 1:[1]

Your approach is ok, your types are wrong. Either you have references:

let my_map_inverted: HashMap<&MyEnum, &String> = my_map.iter()
    .map(|(k, v)| (v, k)).collect();

Playground

Or you Clone the keys and values:

let my_map_inverted: HashMap<MyEnum, String> = my_map.iter()
    .map(|(k, v)| (v.clone(), k.clone())).collect();

Playground

Then you have some other problems, like some calls cannot be used in static context, you can use once_cell for that:

use std::collections::HashMap;
use once_cell::sync::Lazy; // 1.10.0;

// Directive added because of this answer, which eliminated a couple of errors:
// https://github.com/rust-lang/rust/issues/22756#issuecomment-75715762
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
enum MyEnum {
    Value1,
    Value2,
}

static MYMAP: Lazy<HashMap<String, MyEnum>> = Lazy::new(
    || HashMap::from_iter([
        (String::from("value1"), MyEnum::Value1),
        (String::from("value1"), MyEnum::Value1),
]));

static MYMAP_INVERTED: Lazy<HashMap<MyEnum, String>> = Lazy::new(
    || HashMap::from_iter(MYMAP.iter().map(|(k, v)| (v.clone(), k.clone()))
));

Playground

Solution 2:[2]

To swap the keys and values, you would be better off using the from_iter constructor since from can't take a Vec, and collect isn't sure what to do the way it is now. The code to do that would look like this, but you can't use methods like that in a static context, so that will require a refactor:

let MYMAP_INVERTED: HashMap<MyEnum, String> = HashMap::from_iter(
    MYMAP.iter().map(|(k, v)| (*v, k.clone()))
);

The *v and k.clone() are required because the map will return references to the data.

As for the static problem, your best bets would be either using the lazy_static crate or a SyncLazy from the standard library.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Neil
Solution 2 Jeremy Meadows