如何在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 {}
}
}
}
}
答案 0 :(得分:52)
Rust中没有地图文字语法。我不知道完全的原因,但我希望有多个数据结构可以像地图一样(例如BTreeMap
和HashMap
)这样的事实很难选一个。
但是,您可以创建一个宏来为您完成工作,如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和 尼姆: