Skip to content

Commit 28e6093

Browse files
borsgitbot
authored and
gitbot
committed
Auto merge of rust-lang#84111 - bstrie:hashfrom, r=joshtriplett
Stabilize `impl From<[(K, V); N]> for HashMap` (and friends) In addition to allowing HashMap to participate in Into/From conversion, this adds the long-requested ability to use constructor-like syntax for initializing a HashMap: ```rust let map = HashMap::from([ (1, 2), (3, 4), (5, 6) ]); ``` This addition is highly motivated by existing precedence, e.g. it is already possible to similarly construct a Vec from a fixed-size array: ```rust let vec = Vec::from([1, 2, 3]); ``` ...and it is already possible to collect a Vec of tuples into a HashMap (and vice-versa): ```rust let vec = Vec::from([(1, 2)]); let map: HashMap<_, _> = vec.into_iter().collect(); let vec: Vec<(_, _)> = map.into_iter().collect(); ``` ...and of course it is likewise possible to collect a fixed-size array of tuples into a HashMap ([but not vice-versa just yet](rust-lang#81615)): ```rust let arr = [(1, 2)]; let map: HashMap<_, _> = std::array::IntoIter::new(arr).collect(); ``` Therefore this addition seems like a no-brainer. As for any impl, this would be insta-stable.
2 parents ecbd356 + 6a5bb41 commit 28e6093

File tree

12 files changed

+244
-26
lines changed

12 files changed

+244
-26
lines changed

Diff for: alloc/src/collections/binary_heap.rs

+24
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,14 @@ use super::SpecExtend;
209209
/// assert!(heap.is_empty())
210210
/// ```
211211
///
212+
/// A `BinaryHeap` with a known list of items can be initialized from an array:
213+
///
214+
/// ```
215+
/// use std::collections::BinaryHeap;
216+
///
217+
/// let heap = BinaryHeap::from([1, 5, 2]);
218+
/// ```
219+
///
212220
/// ## Min-heap
213221
///
214222
/// Either `std::cmp::Reverse` or a custom `Ord` implementation can be used to
@@ -1466,6 +1474,22 @@ impl<T: Ord> From<Vec<T>> for BinaryHeap<T> {
14661474
}
14671475
}
14681476

1477+
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
1478+
impl<T: Ord, const N: usize> From<[T; N]> for BinaryHeap<T> {
1479+
/// ```
1480+
/// use std::collections::BinaryHeap;
1481+
///
1482+
/// let mut h1 = BinaryHeap::from([1, 4, 2, 3]);
1483+
/// let mut h2: BinaryHeap<_> = [1, 4, 2, 3].into();
1484+
/// while let Some((a, b)) = h1.pop().zip(h2.pop()) {
1485+
/// assert_eq!(a, b);
1486+
/// }
1487+
/// ```
1488+
fn from(arr: [T; N]) -> Self {
1489+
core::array::IntoIter::new(arr).collect()
1490+
}
1491+
}
1492+
14691493
#[stable(feature = "binary_heap_extras_15", since = "1.5.0")]
14701494
impl<T> From<BinaryHeap<T>> for Vec<T> {
14711495
/// Converts a `BinaryHeap<T>` into a `Vec<T>`.

Diff for: alloc/src/collections/btree/map.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,20 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT;
109109
/// }
110110
/// ```
111111
///
112-
/// `BTreeMap` also implements an [`Entry API`], which allows for more complex
112+
/// A `BTreeMap` with a known list of items can be initialized from an array:
113+
///
114+
/// ```
115+
/// use std::collections::BTreeMap;
116+
///
117+
/// let solar_distance = BTreeMap::from([
118+
/// ("Mercury", 0.4),
119+
/// ("Venus", 0.7),
120+
/// ("Earth", 1.0),
121+
/// ("Mars", 1.5),
122+
/// ]);
123+
/// ```
124+
///
125+
/// `BTreeMap` implements an [`Entry API`], which allows for complex
113126
/// methods of getting, setting, updating and removing keys and their values:
114127
///
115128
/// [`Entry API`]: BTreeMap::entry
@@ -2012,6 +2025,20 @@ where
20122025
}
20132026
}
20142027

2028+
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
2029+
impl<K: Ord, V, const N: usize> From<[(K, V); N]> for BTreeMap<K, V> {
2030+
/// ```
2031+
/// use std::collections::BTreeMap;
2032+
///
2033+
/// let map1 = BTreeMap::from([(1, 2), (3, 4)]);
2034+
/// let map2: BTreeMap<_, _> = [(1, 2), (3, 4)].into();
2035+
/// assert_eq!(map1, map2);
2036+
/// ```
2037+
fn from(arr: [(K, V); N]) -> Self {
2038+
core::array::IntoIter::new(arr).collect()
2039+
}
2040+
}
2041+
20152042
impl<K, V> BTreeMap<K, V> {
20162043
/// Gets an iterator over the entries of the map, sorted by key.
20172044
///

Diff for: alloc/src/collections/btree/map/tests.rs

+7
Original file line numberDiff line numberDiff line change
@@ -2173,3 +2173,10 @@ fn test_insert_remove_intertwined_ord_chaos() {
21732173
}
21742174
map.check_invariants();
21752175
}
2176+
2177+
#[test]
2178+
fn from_array() {
2179+
let map = BTreeMap::from([(1, 2), (3, 4)]);
2180+
let unordered_duplicates = BTreeMap::from([(3, 4), (1, 2), (1, 2)]);
2181+
assert_eq!(map, unordered_duplicates);
2182+
}

Diff for: alloc/src/collections/btree/set.rs

+22
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ use super::Recover;
5959
/// println!("{}", book);
6060
/// }
6161
/// ```
62+
///
63+
/// A `BTreeSet` with a known list of items can be initialized from an array:
64+
///
65+
/// ```
66+
/// use std::collections::BTreeSet;
67+
///
68+
/// let set = BTreeSet::from([1, 2, 3]);
69+
/// ```
6270
#[derive(Hash, PartialEq, Eq, Ord, PartialOrd)]
6371
#[stable(feature = "rust1", since = "1.0.0")]
6472
#[cfg_attr(not(test), rustc_diagnostic_item = "BTreeSet")]
@@ -1056,6 +1064,20 @@ impl<T: Ord> FromIterator<T> for BTreeSet<T> {
10561064
}
10571065
}
10581066

1067+
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
1068+
impl<T: Ord, const N: usize> From<[T; N]> for BTreeSet<T> {
1069+
/// ```
1070+
/// use std::collections::BTreeSet;
1071+
///
1072+
/// let set1 = BTreeSet::from([1, 2, 3, 4]);
1073+
/// let set2: BTreeSet<_> = [1, 2, 3, 4].into();
1074+
/// assert_eq!(set1, set2);
1075+
/// ```
1076+
fn from(arr: [T; N]) -> Self {
1077+
core::array::IntoIter::new(arr).collect()
1078+
}
1079+
}
1080+
10591081
#[stable(feature = "rust1", since = "1.0.0")]
10601082
impl<T> IntoIterator for BTreeSet<T> {
10611083
type Item = T;

Diff for: alloc/src/collections/btree/set/tests.rs

+7
Original file line numberDiff line numberDiff line change
@@ -738,3 +738,10 @@ fn test_split_off_large_random_sorted() {
738738
assert!(set.into_iter().eq(data.clone().into_iter().filter(|x| *x < key)));
739739
assert!(right.into_iter().eq(data.into_iter().filter(|x| *x >= key)));
740740
}
741+
742+
#[test]
743+
fn from_array() {
744+
let set = BTreeSet::from([1, 2, 3, 4]);
745+
let unordered_duplicates = BTreeSet::from([4, 1, 4, 3, 2]);
746+
assert_eq!(set, unordered_duplicates);
747+
}

Diff for: alloc/src/collections/linked_list.rs

+21
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ mod tests;
3131
/// The `LinkedList` allows pushing and popping elements at either end
3232
/// in constant time.
3333
///
34+
/// A `LinkedList` with a known list of items can be initialized from an array:
35+
/// ```
36+
/// use std::collections::LinkedList;
37+
///
38+
/// let list = LinkedList::from([1, 2, 3]);
39+
/// ```
40+
///
3441
/// NOTE: It is almost always better to use `Vec` or `VecDeque` because
3542
/// array-based containers are generally faster,
3643
/// more memory efficient, and make better use of CPU cache.
@@ -1759,6 +1766,20 @@ impl<T: Hash> Hash for LinkedList<T> {
17591766
}
17601767
}
17611768

1769+
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
1770+
impl<T, const N: usize> From<[T; N]> for LinkedList<T> {
1771+
/// ```
1772+
/// use std::collections::LinkedList;
1773+
///
1774+
/// let list1 = LinkedList::from([1, 2, 3, 4]);
1775+
/// let list2: LinkedList<_> = [1, 2, 3, 4].into();
1776+
/// assert_eq!(list1, list2);
1777+
/// ```
1778+
fn from(arr: [T; N]) -> Self {
1779+
core::array::IntoIter::new(arr).collect()
1780+
}
1781+
}
1782+
17621783
// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters.
17631784
#[allow(dead_code)]
17641785
fn assert_covariance() {

Diff for: alloc/src/collections/vec_deque/mod.rs

+22
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ const MAXIMUM_ZST_CAPACITY: usize = 1 << (usize::BITS - 1); // Largest possible
6767
/// push onto the back in this manner, and iterating over `VecDeque` goes front
6868
/// to back.
6969
///
70+
/// A `VecDeque` with a known list of items can be initialized from an array:
71+
///
72+
/// ```
73+
/// use std::collections::VecDeque;
74+
///
75+
/// let deq = VecDeque::from([-1, 0, 1]);
76+
/// ```
77+
///
7078
/// Since `VecDeque` is a ring buffer, its elements are not necessarily contiguous
7179
/// in memory. If you want to access the elements as a single slice, such as for
7280
/// efficient sorting, you can use [`make_contiguous`]. It rotates the `VecDeque`
@@ -2916,3 +2924,17 @@ impl<T> From<VecDeque<T>> for Vec<T> {
29162924
}
29172925
}
29182926
}
2927+
2928+
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
2929+
impl<T, const N: usize> From<[T; N]> for VecDeque<T> {
2930+
/// ```
2931+
/// use std::collections::VecDeque;
2932+
///
2933+
/// let deq1 = VecDeque::from([1, 2, 3, 4]);
2934+
/// let deq2: VecDeque<_> = [1, 2, 3, 4].into();
2935+
/// assert_eq!(deq1, deq2);
2936+
/// ```
2937+
fn from(arr: [T; N]) -> Self {
2938+
core::array::IntoIter::new(arr).collect()
2939+
}
2940+
}

Diff for: alloc/src/vec/mod.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,13 @@ mod spec_extend;
174174
/// assert_eq!(vec, [7, 1, 2, 3]);
175175
/// ```
176176
///
177-
/// The [`vec!`] macro is provided to make initialization more convenient:
177+
/// The [`vec!`] macro is provided for convenient initialization:
178178
///
179179
/// ```
180-
/// let mut vec = vec![1, 2, 3];
181-
/// vec.push(4);
182-
/// assert_eq!(vec, [1, 2, 3, 4]);
180+
/// let mut vec1 = vec![1, 2, 3];
181+
/// vec1.push(4);
182+
/// let vec2 = Vec::from([1, 2, 3, 4]);
183+
/// assert_eq!(vec1, vec2);
183184
/// ```
184185
///
185186
/// It can also initialize each element of a `Vec<T>` with a given value.

Diff for: std/src/collections/hash/map.rs

+51-17
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,21 @@ use crate::sys;
124124
/// }
125125
/// ```
126126
///
127-
/// `HashMap` also implements an [`Entry API`](#method.entry), which allows
128-
/// for more complex methods of getting, setting, updating and removing keys and
127+
/// A `HashMap` with a known list of items can be initialized from an array:
128+
///
129+
/// ```
130+
/// use std::collections::HashMap;
131+
///
132+
/// let solar_distance = HashMap::from([
133+
/// ("Mercury", 0.4),
134+
/// ("Venus", 0.7),
135+
/// ("Earth", 1.0),
136+
/// ("Mars", 1.5),
137+
/// ]);
138+
/// ```
139+
///
140+
/// `HashMap` implements an [`Entry API`](#method.entry), which allows
141+
/// for complex methods of getting, setting, updating and removing keys and
129142
/// their values:
130143
///
131144
/// ```
@@ -179,27 +192,17 @@ use crate::sys;
179192
/// }
180193
///
181194
/// // Use a HashMap to store the vikings' health points.
182-
/// let mut vikings = HashMap::new();
183-
///
184-
/// vikings.insert(Viking::new("Einar", "Norway"), 25);
185-
/// vikings.insert(Viking::new("Olaf", "Denmark"), 24);
186-
/// vikings.insert(Viking::new("Harald", "Iceland"), 12);
195+
/// let vikings = HashMap::from([
196+
/// (Viking::new("Einar", "Norway"), 25),
197+
/// (Viking::new("Olaf", "Denmark"), 24),
198+
/// (Viking::new("Harald", "Iceland"), 12),
199+
/// ]);
187200
///
188201
/// // Use derived implementation to print the status of the vikings.
189202
/// for (viking, health) in &vikings {
190203
/// println!("{:?} has {} hp", viking, health);
191204
/// }
192205
/// ```
193-
///
194-
/// A `HashMap` with fixed list of elements can be initialized from an array:
195-
///
196-
/// ```
197-
/// use std::collections::HashMap;
198-
///
199-
/// let timber_resources: HashMap<&str, i32> = [("Norway", 100), ("Denmark", 50), ("Iceland", 10)]
200-
/// .iter().cloned().collect();
201-
/// // use the values stored in map
202-
/// ```
203206
204207
#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_type")]
205208
#[stable(feature = "rust1", since = "1.0.0")]
@@ -1149,6 +1152,37 @@ where
11491152
}
11501153
}
11511154

1155+
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
1156+
// Note: as what is currently the most convenient built-in way to construct
1157+
// a HashMap, a simple usage of this function must not *require* the user
1158+
// to provide a type annotation in order to infer the third type parameter
1159+
// (the hasher parameter, conventionally "S").
1160+
// To that end, this impl is defined using RandomState as the concrete
1161+
// type of S, rather than being generic over `S: BuildHasher + Default`.
1162+
// It is expected that users who want to specify a hasher will manually use
1163+
// `with_capacity_and_hasher`.
1164+
// If type parameter defaults worked on impls, and if type parameter
1165+
// defaults could be mixed with const generics, then perhaps
1166+
// this could be generalized.
1167+
// See also the equivalent impl on HashSet.
1168+
impl<K, V, const N: usize> From<[(K, V); N]> for HashMap<K, V, RandomState>
1169+
where
1170+
K: Eq + Hash,
1171+
{
1172+
/// # Examples
1173+
///
1174+
/// ```
1175+
/// use std::collections::HashMap;
1176+
///
1177+
/// let map1 = HashMap::from([(1, 2), (3, 4)]);
1178+
/// let map2: HashMap<_, _> = [(1, 2), (3, 4)].into();
1179+
/// assert_eq!(map1, map2);
1180+
/// ```
1181+
fn from(arr: [(K, V); N]) -> Self {
1182+
crate::array::IntoIter::new(arr).collect()
1183+
}
1184+
}
1185+
11521186
/// An iterator over the entries of a `HashMap`.
11531187
///
11541188
/// This `struct` is created by the [`iter`] method on [`HashMap`]. See its

Diff for: std/src/collections/hash/map/tests.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1085,3 +1085,15 @@ mod test_drain_filter {
10851085
assert_eq!(map.len(), 2);
10861086
}
10871087
}
1088+
1089+
#[test]
1090+
fn from_array() {
1091+
let map = HashMap::from([(1, 2), (3, 4)]);
1092+
let unordered_duplicates = HashMap::from([(3, 4), (1, 2), (1, 2)]);
1093+
assert_eq!(map, unordered_duplicates);
1094+
1095+
// This next line must infer the hasher type parameter.
1096+
// If you make a change that causes this line to no longer infer,
1097+
// that's a problem!
1098+
let _must_not_require_type_annotation = HashMap::from([(1, 2)]);
1099+
}

Diff for: std/src/collections/hash/set.rs

+33-4
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,12 @@ use super::map::{map_try_reserve_error, RandomState};
9595
/// }
9696
/// ```
9797
///
98-
/// A `HashSet` with fixed list of elements can be initialized from an array:
98+
/// A `HashSet` with a known list of items can be initialized from an array:
9999
///
100100
/// ```
101101
/// use std::collections::HashSet;
102102
///
103-
/// let viking_names: HashSet<&'static str> =
104-
/// [ "Einar", "Olaf", "Harald" ].iter().cloned().collect();
105-
/// // use the values stored in the set
103+
/// let viking_names = HashSet::from(["Einar", "Olaf", "Harald"]);
106104
/// ```
107105
///
108106
/// [hash set]: crate::collections#use-the-set-variant-of-any-of-these-maps-when
@@ -998,6 +996,37 @@ where
998996
}
999997
}
1000998

999+
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
1000+
// Note: as what is currently the most convenient built-in way to construct
1001+
// a HashSet, a simple usage of this function must not *require* the user
1002+
// to provide a type annotation in order to infer the third type parameter
1003+
// (the hasher parameter, conventionally "S").
1004+
// To that end, this impl is defined using RandomState as the concrete
1005+
// type of S, rather than being generic over `S: BuildHasher + Default`.
1006+
// It is expected that users who want to specify a hasher will manually use
1007+
// `with_capacity_and_hasher`.
1008+
// If type parameter defaults worked on impls, and if type parameter
1009+
// defaults could be mixed with const generics, then perhaps
1010+
// this could be generalized.
1011+
// See also the equivalent impl on HashMap.
1012+
impl<T, const N: usize> From<[T; N]> for HashSet<T, RandomState>
1013+
where
1014+
T: Eq + Hash,
1015+
{
1016+
/// # Examples
1017+
///
1018+
/// ```
1019+
/// use std::collections::HashSet;
1020+
///
1021+
/// let set1 = HashSet::from([1, 2, 3, 4]);
1022+
/// let set2: HashSet<_> = [1, 2, 3, 4].into();
1023+
/// assert_eq!(set1, set2);
1024+
/// ```
1025+
fn from(arr: [T; N]) -> Self {
1026+
crate::array::IntoIter::new(arr).collect()
1027+
}
1028+
}
1029+
10011030
#[stable(feature = "rust1", since = "1.0.0")]
10021031
impl<T, S> Extend<T> for HashSet<T, S>
10031032
where

0 commit comments

Comments
 (0)