Rust没有字面量语法来表示map。我不知道确切的原因,但我认为有多个类似map的数据结构(例如BTreeMap
和HashMap
)会使选择一个变得困难。
Rust 1.56
现在许多集合都提供了使用From
或Into
从数组参数进行转换的功能:
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
fn main() {
let s = Vec::from([1, 2, 3]);
println!("{:?}", s);
let s = BTreeSet::from([1, 2, 3]);
println!("{:?}", s);
let s = HashSet::from([1, 2, 3]);
println!("{:?}", s);
let s = BTreeMap::from([(1, 2), (3, 4)]);
println!("{:?}", s);
let s = HashMap::from([(1, 2), (3, 4)]);
println!("{:?}", s);
}
这个逻辑可以封装回宏中,以实现某些语法糖:
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
macro_rules! collection {
($($k:expr => $v:expr),* $(,)?) => {{
core::convert::From::from([$(($k, $v),)*])
}};
($($v:expr),* $(,)?) => {{
core::convert::From::from([$($v,)*])
}};
}
fn main() {
let s: Vec<_> = collection![1, 2, 3];
println!("{:?}", s);
let s: BTreeSet<_> = collection! { 1, 2, 3 };
println!("{:?}", s);
let s: HashSet<_> = collection! { 1, 2, 3 };
println!("{:?}", s);
let s: BTreeMap<_, _> = collection! { 1 => 2, 3 => 4 };
println!("{:?}", s);
let s: HashMap<_, _> = collection! { 1 => 2, 3 => 4 };
println!("{:?}", s);
}
Rust 1.51
从 Rust 1.51 开始,您可以使用按值数组迭代器和FromIterator
来收集多种类型的集合:
use std::array::IntoIter;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::iter::FromIterator;
fn main() {
let s = Vec::from_iter([1, 2, 3]);
println!("{:?}", s);
let s = Vec::from_iter(IntoIter::new([1, 2, 3]));
println!("{:?}", s);
let s = BTreeSet::from_iter(IntoIter::new([1, 2, 3]));
println!("{:?}", s);
let s = HashSet::<_>::from_iter(IntoIter::new([1, 2, 3]));
println!("{:?}", s);
let s = BTreeMap::from_iter(IntoIter::new([(1, 2), (3, 4)]));
println!("{:?}", s);
let s = HashMap::<_, _>::from_iter(IntoIter::new([(1, 2), (3, 4)]));
println!("{:?}", s);
}
请注意,在Rust 1.53中,std :: array :: IntoIter
并不总是需要的。
这个逻辑可以包装到一个宏中,以获得一些语法糖:
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
macro_rules! collection {
($($k:expr => $v:expr),* $(,)?) => {{
use std::iter::{Iterator, IntoIterator};
Iterator::collect(IntoIterator::into_iter([$(($k, $v),)*]))
}};
($($v:expr),* $(,)?) => {{
use std::iter::{Iterator, IntoIterator};
Iterator::collect(IntoIterator::into_iter([$($v,)*]))
}};
}
fn main() {
let s: Vec<_> = collection![1, 2, 3];
println!("{:?}", s);
let s: BTreeSet<_> = collection! { 1, 2, 3 };
println!("{:?}", s);
let s: HashSet<_> = collection! { 1, 2, 3 };
println!("{:?}", s);
let s: BTreeMap<_, _> = collection! { 1 => 2, 3 => 4 };
println!("{:?}", s);
let s: HashMap<_, _> = collection! { 1 => 2, 3 => 4 };
println!("{:?}", s);
}
这些解决方案避免了不必要的分配和重新分配。
另请参阅:
以前的版本
您可以创建一个宏来为您完成这项工作,就像在为什么这个Rust HashMap宏不再起作用?中所示。以下是该宏的简化版本,并具有足够的结构使其在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));
}
这个宏避免了分配不必要的中间 Vec
,但它没有使用HashMap::with_capacity
,因此在添加值时可能会有一些无用的 HashMap
重新分配。该宏的更复杂版本可以计算值,但是性能收益可能对大多数宏的使用者没有益处。
grabbag_macros
crate 中也有一个类似的东西。你可以在这里看到源代码: https://github.com/DanielKeep/rust-grabbag/blob/master/grabbag_macros/src/lib.rs#L3-L64。 - DK.DictionaryLiteral
可用于初始化任何符合ExpressibleByDictionaryLiteral
的类型(尽管标准库已经提供了一个这样的类型——Dictionary
)。 - Alexanderstd::array::IntoIter
的稳定,您不再需要使用 nightly 来使用第二种方法。 - AlexHashMap::from([ ("k1",1), ("k2",2) ])
。 - Haider