如何创建HashMap文字?

时间:2014-12-20 17:45:55

标签: hashmap rust

如何在Rust中创建HashMap文字?在Python中,我可以这样做:

hashmap = {
   'element0': {
       'name': 'My New Element',
       'childs': {
           'child0': {
               'name': 'Child For Element 0',
               'childs': {
                   ...
               }
           }
       }
   },
   ...
}

在Go中就像这样:

type Node struct {
    name string
    childs map[string]Node
}

hashmap := map[string]Node {
    "element0": Node{
        "My New Element",
        map[string]Node {
            'child0': Node{
                "Child For Element 0",
                map[string]Node {}
            }
        }
    }
}

6 个答案:

答案 0 :(得分:52)

Rust中没有地图文字语法。我不知道完全的原因,但我希望有多个数据结构可以像地图一样(例如BTreeMapHashMap)这样的事实很难选一个。

但是,您可以创建一个宏来为您完成工作,如Why does this rust HashMap macro no longer work?中所示。这个宏被简化了一点,并且有足够的结构使它成为runnable in the playground

macro_rules! map(
    { $($key:expr => $value:expr),+ } => {
        {
            let mut m = ::std::collections::HashMap::new();
            $(
                m.insert($key, $value);
            )+
            m
        }
     };
);

fn main() {
    let names = map!{ 1 => "one", 2 => "two" };
    println!("{} -> {:?}", 1, names.get(&1));
    println!("{} -> {:?}", 10, names.get(&10));
}

答案 1 :(得分:21)

我推荐maplit包。

引用文档:

  

具有特定类型的容器文字的宏。

#[macro_use] extern crate maplit;

let map = hashmap!{
    "a" => 1,
    "b" => 2,
};
  

maplit crate使用=>映射宏的语法。由于语法上常规macro_rules的限制,不可能使用:as separator作为分隔符!宏。

     

请注意,生锈宏很灵活,您可以使用哪些括号进行调用。您可以将它们用作hashmap!{}或hashmap![]或hashmap!()。这个箱子建议{}作为地图的惯例&设置宏,它匹配它们的Debug输出。

     

     

btreemap从键值对列表中创建BTreeMap

     

btreeset从元素列表中创建BTreeSet。

     

hashmap从键值对列表中创建HashMap

     

hashset从元素列表中创建一个HashSet。

答案 2 :(得分:13)

有一个如何在documentation for HashMap中实现这一目的的例子:

 let timber_resources: HashMap<&str, i32> =
    [("Norway", 100),
     ("Denmark", 50),
     ("Iceland", 10)]
    .iter().cloned().collect();

答案 3 :(得分:1)

您可以使用velcro条板箱*。正如其他答案中所建议的,它类似于maplit,但是具有更多的集合类型,更好的语法(至少在我看来!)和更多的功能。

假设您要使用String而不是&str,那么您的确切示例将如下所示:

use std::collections::HashMap;
use velcro::hash_map;

struct Node {
    name: String
    children: HashMap<String, Node>,
}

let map = hash_map! {
    String::from("element0"): Node {
        name: "My New Element".into(),
        children: hash_map! {
            String::from("child0"): Node {
                name: "child0".into(),
                children: hash_map!{}
            }
        }
    }
};

由于String的构造方式,这有点丑陋。但是可以通过使用hash_map_from!使其变得更整洁,而无需更改密钥类型,该方法将自动进行转换:

use std::collections::HashMap;
use velcro::{hash_map, hash_map_from};

let map: HashMap<String, Node> = hash_map_from! {
    "element0": Node {
        name: "My New Element".into(),
        children: hash_map_from! {
            "child0": Node {
                name: "child0".into(),
                children: hash_map!{}
            }
        }
    }
};

没有什么比Go版本更冗长了。


* 完整披露:我是此板条箱的作者。

答案 4 :(得分:0)

对于一个元素

如果您希望只用一行中的一个元素来初始化地图(并且代码中没有可见的突变),则可以执行以下操作:

let map: HashMap<&'static str, u32> = Some(("answer", 42)).into_iter().collect();

这要归功于Option能够使用Iterator成为into_iter()的有用性。

在实际代码中,您可能不需要帮助编译器使用以下类型:

use std::collections::HashMap;

fn john_wick() -> HashMap<&'static str, u32> {
    Some(("answer", 42)).into_iter().collect()
}

fn main() {
    let result = john_wick();

    let mut expected = HashMap::new();
    expected.insert("answer", 42);

    assert_eq!(result, expected);
}

还有一种方法可以将其链接起来,以使多个元素执行类似Some(a).into_iter().chain(Some(b).into_iter()).collect()的操作,但这更长,可读性更差,并且可能存在一些优化问题,因此我建议不要这样做。

答案 5 :(得分:-1)

我看到了很多不错的解决方案,但是我只想简单一些。至 为此,这是一个特质:

use std::collections::HashMap;

trait Hash {
   fn to_map(&self) -> HashMap<&str, u16>;
}

impl Hash for [(&str, u16)] {
   fn to_map(&self) -> HashMap<&str, u16> {
      self.iter().cloned().collect()
   }
}

fn main() {
   let m = [("year", 2019), ("month", 12)].to_map();
   println!("{:?}", m)
}

我认为这是一个不错的选择,因为它实质上是Ruby和 尼姆: