В Rust нет синтаксиса литерала карты. Я не знаю точной причины, но полагаю, что тот факт, что существует несколько структур данных, которые действуют подобно карте (например, обе BTreeMap
и HashMap
), затруднит выбор одной.
Однако вы можете создать макрос, который сделает эту работу за вас, как показано в разделе Почему этот макрос rust HashMap больше не работает? . Вот этот макрос немного упрощенный и с достаточной структурой, чтобы его можно было запускать на игровой площадке :
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
добавляемых значений as. Возможна более сложная версия макроса, которая подсчитывает значения, но преимущества в производительности, вероятно, не то, от чего выиграет большинство применений макроса.
В ночной версии Rust вы можете избежать как ненужного выделения (и перераспределения!), Так и необходимости в макросе:
#![feature(array_value_iter)]
use std::array::IntoIter;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::iter::FromIterator;
fn main() {
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);
}
Затем эту логику можно снова обернуть в макрос:
#![feature(array_value_iter)]
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
macro_rules! collection {
($($k:expr => $v:expr),* $(,)?) => {
std::iter::Iterator::collect(std::array::IntoIter::new([$(($k, $v),)*]))
};
($($v:expr),* $(,)?) => {
std::iter::Iterator::collect(std::array::IntoIter::new([$($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);
}
Смотрите также:
grabbag_macros
ящике. Вы можете увидеть источник здесь: github.com/DanielKeep/rust-grabbag/blob/master/grabbag_macros/… .DictionaryLiteral
может использоваться для инициализации любого типа, который соответствуетExpressibleByDictionaryLiteral
(даже если стандартная библиотека предлагает один такой типDictionary
),Я рекомендую обрешетку из клена .
Цитата из документации:
use maplit::hashmap; let map = hashmap!{ "a" => 1, "b" => 2, };
источник
В документации для
HashMap
:let timber_resources: HashMap<&str, i32> = [("Norway", 100), ("Denmark", 50), ("Iceland", 10)] .iter() .cloned() .collect();
источник
String
вместо&str
.vec![ (name, value) ].into_iter().collect()
Как отметил @Johannes в комментариях, его можно использовать,
vec![]
потому что:Vec<T>
реализуетIntoIterator<T>
чертуHashMap<K, V>
орудияFromIterator<Item = (K, V)>
что означает, что вы можете сделать это:
let map: HashMap<String, String> = vec![("key".to_string(), "value".to_string())] .into_iter() .collect();
Вы можете использовать,
&str
но вам может потребоваться аннотировать время жизни, если это не так'static
:let map: HashMap<&str, usize> = vec![("one", 1), ("two", 2)].into_iter().collect();
источник
Можно использовать
velcro
ящик *. Это похоже на тоmaplit
, что рекомендовано в других ответах, но с большим количеством типов коллекций, лучшим синтаксисом (по крайней мере, на мой взгляд!) И большим количеством функций.Предполагая, что вы хотите использовать
String
s, а не&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
s. Но его можно сделать немного чище, не меняя тип ключа, с помощью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.
* Полное раскрытие: я автор этого ящика.
источник
За один элемент
Если вы хотите инициализировать карту только с одним элементом в одной строке (и без видимых изменений в вашем коде), вы можете сделать:
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()
, но это длиннее, менее читабельно и, вероятно, имеет некоторые проблемы с оптимизацией, поэтому я не советую этого.источник
Я видел кучу причудливых решений, но мне просто хотелось чего-то простого. С этой целью вот черта:
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 и Nim:
источник
copied()
запретить тип, который не реализует копирование, избегая клонирования ни за что, или, по крайней мере, сделать его явным, используя cloned_to_map (), если вы также хотите иметь возможность делать это только с клонированным типом.