How do I create HashMap<&str, Cow<str>> from HashMap<String, String> using iter().map()

Hi,
I'm using this crate https://crates.io/crates/oauth1 and want to pass a HashMap<String, String> as params so I need to convert it to HashMap<&str, Cow<str>>. However when I using iter().map() I got the error below. How do I implement that FromIterator trait as compiler said?
Thanks in advance.

use std::borrow::Cow;
use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert("hello".to_string(), "1".to_string());
    map.insert("world".to_string(), "2".to_string());

    println!("{:?}", map);

    let params: HashMap<&str, Cow<str>> = map
        .iter()
        .map(|(key, value)| (key, Cow::from(value)))
        .collect();
    println!("{:?}", params);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0277]: a value of type `std::collections::HashMap<&str, std::borrow::Cow<'_, str>>` cannot be built from an iterator over elements of type `(&std::string::String, std::borrow::Cow<'_, str>)`
  --> src/main.rs:14:10
   |
14 |         .collect();
   |          ^^^^^^^ value of type `std::collections::HashMap<&str, std::borrow::Cow<'_, str>>` cannot be built from `std::iter::Iterator<Item=(&std::string::String, std::borrow::Cow<'_, str>)>`
   |
   = help: the trait `std::iter::FromIterator<(&std::string::String, std::borrow::Cow<'_, str>)>` is not implemented for `std::collections::HashMap<&str, std::borrow::Cow<'_, str>>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

You need to convert the &String to a &str.

let params: HashMap<&str, Cow<str>> = map
    .iter()
-   .map(|(key, value)| (key, Cow::from(value)))
+   .map(|(key, value)| (key.as_str(), Cow::from(value)))
    .collect();

playground

1 Like

The problem is that .iter() gives you an iterator over (&String, &String), so key ends up with type &String, and when you collect it complains because the type needs to be &str, not &String.

There are several ways to convert from &String to &str. You usually don't need to do this, since the compiler can infer that you want &str and "auto dereference" &String into &str. But in this case, creating an iterator over &String is totally valid, so it won't change that.

I think the most obvious method is, as @alice mentioned, to use String::as_str(), so key.as_str() in your map. Should also work to do key.as_ref(), or &**key (the first * gets rid of the &, and the second * explicitly transforms String into str using Deref).

1 Like

Thanks a lot @alice for solution and @daboross for detailed explanation.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.