mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 00:34:06 +00:00
Auto merge of #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](https://github.com/rust-lang/rust/issues/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.
This commit is contained in:
commit
2b4196e977
@ -209,6 +209,14 @@ use super::SpecExtend;
|
||||
/// assert!(heap.is_empty())
|
||||
/// ```
|
||||
///
|
||||
/// A `BinaryHeap` with a known list of items can be initialized from an array:
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::BinaryHeap;
|
||||
///
|
||||
/// let heap = BinaryHeap::from([1, 5, 2]);
|
||||
/// ```
|
||||
///
|
||||
/// ## Min-heap
|
||||
///
|
||||
/// Either `std::cmp::Reverse` or a custom `Ord` implementation can be used to
|
||||
@ -1465,6 +1473,22 @@ impl<T: Ord> From<Vec<T>> for BinaryHeap<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
|
||||
impl<T: Ord, const N: usize> From<[T; N]> for BinaryHeap<T> {
|
||||
/// ```
|
||||
/// use std::collections::BinaryHeap;
|
||||
///
|
||||
/// let mut h1 = BinaryHeap::from([1, 4, 2, 3]);
|
||||
/// let mut h2: BinaryHeap<_> = [1, 4, 2, 3].into();
|
||||
/// while let Some((a, b)) = h1.pop().zip(h2.pop()) {
|
||||
/// assert_eq!(a, b);
|
||||
/// }
|
||||
/// ```
|
||||
fn from(arr: [T; N]) -> Self {
|
||||
core::array::IntoIter::new(arr).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "binary_heap_extras_15", since = "1.5.0")]
|
||||
impl<T> From<BinaryHeap<T>> for Vec<T> {
|
||||
/// Converts a `BinaryHeap<T>` into a `Vec<T>`.
|
||||
|
@ -109,7 +109,20 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// `BTreeMap` also implements an [`Entry API`], which allows for more complex
|
||||
/// A `BTreeMap` with a known list of items can be initialized from an array:
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::BTreeMap;
|
||||
///
|
||||
/// let solar_distance = BTreeMap::from([
|
||||
/// ("Mercury", 0.4),
|
||||
/// ("Venus", 0.7),
|
||||
/// ("Earth", 1.0),
|
||||
/// ("Mars", 1.5),
|
||||
/// ]);
|
||||
/// ```
|
||||
///
|
||||
/// `BTreeMap` implements an [`Entry API`], which allows for complex
|
||||
/// methods of getting, setting, updating and removing keys and their values:
|
||||
///
|
||||
/// [`Entry API`]: BTreeMap::entry
|
||||
@ -2012,6 +2025,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
|
||||
impl<K: Ord, V, const N: usize> From<[(K, V); N]> for BTreeMap<K, V> {
|
||||
/// ```
|
||||
/// use std::collections::BTreeMap;
|
||||
///
|
||||
/// let map1 = BTreeMap::from([(1, 2), (3, 4)]);
|
||||
/// let map2: BTreeMap<_, _> = [(1, 2), (3, 4)].into();
|
||||
/// assert_eq!(map1, map2);
|
||||
/// ```
|
||||
fn from(arr: [(K, V); N]) -> Self {
|
||||
core::array::IntoIter::new(arr).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> BTreeMap<K, V> {
|
||||
/// Gets an iterator over the entries of the map, sorted by key.
|
||||
///
|
||||
|
@ -2173,3 +2173,10 @@ fn test_insert_remove_intertwined_ord_chaos() {
|
||||
}
|
||||
map.check_invariants();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_array() {
|
||||
let map = BTreeMap::from([(1, 2), (3, 4)]);
|
||||
let unordered_duplicates = BTreeMap::from([(3, 4), (1, 2), (1, 2)]);
|
||||
assert_eq!(map, unordered_duplicates);
|
||||
}
|
||||
|
@ -59,6 +59,14 @@ use super::Recover;
|
||||
/// println!("{}", book);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// A `BTreeSet` with a known list of items can be initialized from an array:
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::BTreeSet;
|
||||
///
|
||||
/// let set = BTreeSet::from([1, 2, 3]);
|
||||
/// ```
|
||||
#[derive(Hash, PartialEq, Eq, Ord, PartialOrd)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "BTreeSet")]
|
||||
@ -1057,6 +1065,20 @@ impl<T: Ord> FromIterator<T> for BTreeSet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
|
||||
impl<T: Ord, const N: usize> From<[T; N]> for BTreeSet<T> {
|
||||
/// ```
|
||||
/// use std::collections::BTreeSet;
|
||||
///
|
||||
/// let set1 = BTreeSet::from([1, 2, 3, 4]);
|
||||
/// let set2: BTreeSet<_> = [1, 2, 3, 4].into();
|
||||
/// assert_eq!(set1, set2);
|
||||
/// ```
|
||||
fn from(arr: [T; N]) -> Self {
|
||||
core::array::IntoIter::new(arr).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> IntoIterator for BTreeSet<T> {
|
||||
type Item = T;
|
||||
|
@ -738,3 +738,10 @@ fn test_split_off_large_random_sorted() {
|
||||
assert!(set.into_iter().eq(data.clone().into_iter().filter(|x| *x < key)));
|
||||
assert!(right.into_iter().eq(data.into_iter().filter(|x| *x >= key)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_array() {
|
||||
let set = BTreeSet::from([1, 2, 3, 4]);
|
||||
let unordered_duplicates = BTreeSet::from([4, 1, 4, 3, 2]);
|
||||
assert_eq!(set, unordered_duplicates);
|
||||
}
|
||||
|
@ -31,6 +31,13 @@ mod tests;
|
||||
/// The `LinkedList` allows pushing and popping elements at either end
|
||||
/// in constant time.
|
||||
///
|
||||
/// A `LinkedList` with a known list of items can be initialized from an array:
|
||||
/// ```
|
||||
/// use std::collections::LinkedList;
|
||||
///
|
||||
/// let list = LinkedList::from([1, 2, 3]);
|
||||
/// ```
|
||||
///
|
||||
/// NOTE: It is almost always better to use `Vec` or `VecDeque` because
|
||||
/// array-based containers are generally faster,
|
||||
/// more memory efficient, and make better use of CPU cache.
|
||||
@ -1901,6 +1908,20 @@ impl<T: Hash> Hash for LinkedList<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
|
||||
impl<T, const N: usize> From<[T; N]> for LinkedList<T> {
|
||||
/// ```
|
||||
/// use std::collections::LinkedList;
|
||||
///
|
||||
/// let list1 = LinkedList::from([1, 2, 3, 4]);
|
||||
/// let list2: LinkedList<_> = [1, 2, 3, 4].into();
|
||||
/// assert_eq!(list1, list2);
|
||||
/// ```
|
||||
fn from(arr: [T; N]) -> Self {
|
||||
core::array::IntoIter::new(arr).collect()
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters.
|
||||
#[allow(dead_code)]
|
||||
fn assert_covariance() {
|
||||
|
@ -67,6 +67,14 @@ const MAXIMUM_ZST_CAPACITY: usize = 1 << (usize::BITS - 1); // Largest possible
|
||||
/// push onto the back in this manner, and iterating over `VecDeque` goes front
|
||||
/// to back.
|
||||
///
|
||||
/// A `VecDeque` with a known list of items can be initialized from an array:
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::VecDeque;
|
||||
///
|
||||
/// let deq = VecDeque::from([-1, 0, 1]);
|
||||
/// ```
|
||||
///
|
||||
/// Since `VecDeque` is a ring buffer, its elements are not necessarily contiguous
|
||||
/// in memory. If you want to access the elements as a single slice, such as for
|
||||
/// efficient sorting, you can use [`make_contiguous`]. It rotates the `VecDeque`
|
||||
@ -2915,3 +2923,17 @@ impl<T> From<VecDeque<T>> for Vec<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
|
||||
impl<T, const N: usize> From<[T; N]> for VecDeque<T> {
|
||||
/// ```
|
||||
/// use std::collections::VecDeque;
|
||||
///
|
||||
/// let deq1 = VecDeque::from([1, 2, 3, 4]);
|
||||
/// let deq2: VecDeque<_> = [1, 2, 3, 4].into();
|
||||
/// assert_eq!(deq1, deq2);
|
||||
/// ```
|
||||
fn from(arr: [T; N]) -> Self {
|
||||
core::array::IntoIter::new(arr).collect()
|
||||
}
|
||||
}
|
||||
|
@ -174,12 +174,13 @@ mod spec_extend;
|
||||
/// assert_eq!(vec, [7, 1, 2, 3]);
|
||||
/// ```
|
||||
///
|
||||
/// The [`vec!`] macro is provided to make initialization more convenient:
|
||||
/// The [`vec!`] macro is provided for convenient initialization:
|
||||
///
|
||||
/// ```
|
||||
/// let mut vec = vec![1, 2, 3];
|
||||
/// vec.push(4);
|
||||
/// assert_eq!(vec, [1, 2, 3, 4]);
|
||||
/// let mut vec1 = vec![1, 2, 3];
|
||||
/// vec1.push(4);
|
||||
/// let vec2 = Vec::from([1, 2, 3, 4]);
|
||||
/// assert_eq!(vec1, vec2);
|
||||
/// ```
|
||||
///
|
||||
/// It can also initialize each element of a `Vec<T>` with a given value.
|
||||
|
@ -124,8 +124,21 @@ use crate::sys;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// `HashMap` also implements an [`Entry API`](#method.entry), which allows
|
||||
/// for more complex methods of getting, setting, updating and removing keys and
|
||||
/// A `HashMap` with a known list of items can be initialized from an array:
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
/// let solar_distance = HashMap::from([
|
||||
/// ("Mercury", 0.4),
|
||||
/// ("Venus", 0.7),
|
||||
/// ("Earth", 1.0),
|
||||
/// ("Mars", 1.5),
|
||||
/// ]);
|
||||
/// ```
|
||||
///
|
||||
/// `HashMap` implements an [`Entry API`](#method.entry), which allows
|
||||
/// for complex methods of getting, setting, updating and removing keys and
|
||||
/// their values:
|
||||
///
|
||||
/// ```
|
||||
@ -179,27 +192,17 @@ use crate::sys;
|
||||
/// }
|
||||
///
|
||||
/// // Use a HashMap to store the vikings' health points.
|
||||
/// let mut vikings = HashMap::new();
|
||||
///
|
||||
/// vikings.insert(Viking::new("Einar", "Norway"), 25);
|
||||
/// vikings.insert(Viking::new("Olaf", "Denmark"), 24);
|
||||
/// vikings.insert(Viking::new("Harald", "Iceland"), 12);
|
||||
/// let vikings = HashMap::from([
|
||||
/// (Viking::new("Einar", "Norway"), 25),
|
||||
/// (Viking::new("Olaf", "Denmark"), 24),
|
||||
/// (Viking::new("Harald", "Iceland"), 12),
|
||||
/// ]);
|
||||
///
|
||||
/// // Use derived implementation to print the status of the vikings.
|
||||
/// for (viking, health) in &vikings {
|
||||
/// println!("{:?} has {} hp", viking, health);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// A `HashMap` with fixed list of elements can be initialized from an array:
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
/// let timber_resources: HashMap<&str, i32> = [("Norway", 100), ("Denmark", 50), ("Iceland", 10)]
|
||||
/// .iter().cloned().collect();
|
||||
/// // use the values stored in map
|
||||
/// ```
|
||||
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_type")]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
@ -1148,6 +1151,37 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
|
||||
// Note: as what is currently the most convenient built-in way to construct
|
||||
// a HashMap, a simple usage of this function must not *require* the user
|
||||
// to provide a type annotation in order to infer the third type parameter
|
||||
// (the hasher parameter, conventionally "S").
|
||||
// To that end, this impl is defined using RandomState as the concrete
|
||||
// type of S, rather than being generic over `S: BuildHasher + Default`.
|
||||
// It is expected that users who want to specify a hasher will manually use
|
||||
// `with_capacity_and_hasher`.
|
||||
// If type parameter defaults worked on impls, and if type parameter
|
||||
// defaults could be mixed with const generics, then perhaps
|
||||
// this could be generalized.
|
||||
// See also the equivalent impl on HashSet.
|
||||
impl<K, V, const N: usize> From<[(K, V); N]> for HashMap<K, V, RandomState>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
{
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
/// let map1 = HashMap::from([(1, 2), (3, 4)]);
|
||||
/// let map2: HashMap<_, _> = [(1, 2), (3, 4)].into();
|
||||
/// assert_eq!(map1, map2);
|
||||
/// ```
|
||||
fn from(arr: [(K, V); N]) -> Self {
|
||||
crate::array::IntoIter::new(arr).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the entries of a `HashMap`.
|
||||
///
|
||||
/// This `struct` is created by the [`iter`] method on [`HashMap`]. See its
|
||||
|
@ -1085,3 +1085,15 @@ mod test_drain_filter {
|
||||
assert_eq!(map.len(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_array() {
|
||||
let map = HashMap::from([(1, 2), (3, 4)]);
|
||||
let unordered_duplicates = HashMap::from([(3, 4), (1, 2), (1, 2)]);
|
||||
assert_eq!(map, unordered_duplicates);
|
||||
|
||||
// This next line must infer the hasher type parameter.
|
||||
// If you make a change that causes this line to no longer infer,
|
||||
// that's a problem!
|
||||
let _must_not_require_type_annotation = HashMap::from([(1, 2)]);
|
||||
}
|
||||
|
@ -95,14 +95,12 @@ use super::map::{map_try_reserve_error, RandomState};
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// A `HashSet` with fixed list of elements can be initialized from an array:
|
||||
/// A `HashSet` with a known list of items can be initialized from an array:
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashSet;
|
||||
///
|
||||
/// let viking_names: HashSet<&'static str> =
|
||||
/// [ "Einar", "Olaf", "Harald" ].iter().cloned().collect();
|
||||
/// // use the values stored in the set
|
||||
/// let viking_names = HashSet::from(["Einar", "Olaf", "Harald"]);
|
||||
/// ```
|
||||
///
|
||||
/// [hash set]: crate::collections#use-the-set-variant-of-any-of-these-maps-when
|
||||
@ -997,6 +995,37 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
|
||||
// Note: as what is currently the most convenient built-in way to construct
|
||||
// a HashSet, a simple usage of this function must not *require* the user
|
||||
// to provide a type annotation in order to infer the third type parameter
|
||||
// (the hasher parameter, conventionally "S").
|
||||
// To that end, this impl is defined using RandomState as the concrete
|
||||
// type of S, rather than being generic over `S: BuildHasher + Default`.
|
||||
// It is expected that users who want to specify a hasher will manually use
|
||||
// `with_capacity_and_hasher`.
|
||||
// If type parameter defaults worked on impls, and if type parameter
|
||||
// defaults could be mixed with const generics, then perhaps
|
||||
// this could be generalized.
|
||||
// See also the equivalent impl on HashMap.
|
||||
impl<T, const N: usize> From<[T; N]> for HashSet<T, RandomState>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
{
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashSet;
|
||||
///
|
||||
/// let set1 = HashSet::from([1, 2, 3, 4]);
|
||||
/// let set2: HashSet<_> = [1, 2, 3, 4].into();
|
||||
/// assert_eq!(set1, set2);
|
||||
/// ```
|
||||
fn from(arr: [T; N]) -> Self {
|
||||
crate::array::IntoIter::new(arr).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T, S> Extend<T> for HashSet<T, S>
|
||||
where
|
||||
|
@ -484,3 +484,15 @@ fn test_drain_filter_pred_panic_leak() {
|
||||
assert_eq!(DROPS.load(Ordering::SeqCst), 3);
|
||||
assert_eq!(set.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_array() {
|
||||
let set = HashSet::from([1, 2, 3, 4]);
|
||||
let unordered_duplicates = HashSet::from([4, 1, 4, 3, 2]);
|
||||
assert_eq!(set, unordered_duplicates);
|
||||
|
||||
// This next line must infer the hasher type parameter.
|
||||
// If you make a change that causes this line to no longer infer,
|
||||
// that's a problem!
|
||||
let _must_not_require_type_annotation = HashSet::from([1, 2]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user