]> git.proxmox.com Git - rustc.git/blob - vendor/hashbrown-0.9.1/src/external_trait_impls/rayon/map.rs
New upstream version 1.54.0+dfsg1
[rustc.git] / vendor / hashbrown-0.9.1 / src / external_trait_impls / rayon / map.rs
1 //! Rayon extensions for `HashMap`.
2
3 use crate::hash_map::HashMap;
4 use core::fmt;
5 use core::hash::{BuildHasher, Hash};
6 use rayon::iter::plumbing::UnindexedConsumer;
7 use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelExtend, ParallelIterator};
8
9 /// Parallel iterator over shared references to entries in a map.
10 ///
11 /// This iterator is created by the [`par_iter`] method on [`HashMap`]
12 /// (provided by the [`IntoParallelRefIterator`] trait).
13 /// See its documentation for more.
14 ///
15 /// [`par_iter`]: /hashbrown/struct.HashMap.html#method.par_iter
16 /// [`HashMap`]: /hashbrown/struct.HashMap.html
17 /// [`IntoParallelRefIterator`]: https://docs.rs/rayon/1.0/rayon/iter/trait.IntoParallelRefIterator.html
18 pub struct ParIter<'a, K, V, S> {
19 map: &'a HashMap<K, V, S>,
20 }
21
22 impl<'a, K: Sync, V: Sync, S: Sync> ParallelIterator for ParIter<'a, K, V, S> {
23 type Item = (&'a K, &'a V);
24
25 #[cfg_attr(feature = "inline-more", inline)]
26 fn drive_unindexed<C>(self, consumer: C) -> C::Result
27 where
28 C: UnindexedConsumer<Self::Item>,
29 {
30 unsafe { self.map.table.par_iter() }
31 .map(|x| unsafe {
32 let r = x.as_ref();
33 (&r.0, &r.1)
34 })
35 .drive_unindexed(consumer)
36 }
37 }
38
39 impl<K, V, S> Clone for ParIter<'_, K, V, S> {
40 #[cfg_attr(feature = "inline-more", inline)]
41 fn clone(&self) -> Self {
42 ParIter { map: self.map }
43 }
44 }
45
46 impl<K: fmt::Debug + Eq + Hash, V: fmt::Debug, S: BuildHasher> fmt::Debug for ParIter<'_, K, V, S> {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 self.map.iter().fmt(f)
49 }
50 }
51
52 /// Parallel iterator over shared references to keys in a map.
53 ///
54 /// This iterator is created by the [`par_keys`] method on [`HashMap`].
55 /// See its documentation for more.
56 ///
57 /// [`par_keys`]: /hashbrown/struct.HashMap.html#method.par_keys
58 /// [`HashMap`]: /hashbrown/struct.HashMap.html
59 pub struct ParKeys<'a, K, V, S> {
60 map: &'a HashMap<K, V, S>,
61 }
62
63 impl<'a, K: Sync, V: Sync, S: Sync> ParallelIterator for ParKeys<'a, K, V, S> {
64 type Item = &'a K;
65
66 #[cfg_attr(feature = "inline-more", inline)]
67 fn drive_unindexed<C>(self, consumer: C) -> C::Result
68 where
69 C: UnindexedConsumer<Self::Item>,
70 {
71 unsafe { self.map.table.par_iter() }
72 .map(|x| unsafe { &x.as_ref().0 })
73 .drive_unindexed(consumer)
74 }
75 }
76
77 impl<K, V, S> Clone for ParKeys<'_, K, V, S> {
78 #[cfg_attr(feature = "inline-more", inline)]
79 fn clone(&self) -> Self {
80 ParKeys { map: self.map }
81 }
82 }
83
84 impl<K: fmt::Debug + Eq + Hash, V, S: BuildHasher> fmt::Debug for ParKeys<'_, K, V, S> {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 self.map.keys().fmt(f)
87 }
88 }
89
90 /// Parallel iterator over shared references to values in a map.
91 ///
92 /// This iterator is created by the [`par_values`] method on [`HashMap`].
93 /// See its documentation for more.
94 ///
95 /// [`par_values`]: /hashbrown/struct.HashMap.html#method.par_values
96 /// [`HashMap`]: /hashbrown/struct.HashMap.html
97 pub struct ParValues<'a, K, V, S> {
98 map: &'a HashMap<K, V, S>,
99 }
100
101 impl<'a, K: Sync, V: Sync, S: Sync> ParallelIterator for ParValues<'a, K, V, S> {
102 type Item = &'a V;
103
104 #[cfg_attr(feature = "inline-more", inline)]
105 fn drive_unindexed<C>(self, consumer: C) -> C::Result
106 where
107 C: UnindexedConsumer<Self::Item>,
108 {
109 unsafe { self.map.table.par_iter() }
110 .map(|x| unsafe { &x.as_ref().1 })
111 .drive_unindexed(consumer)
112 }
113 }
114
115 impl<K, V, S> Clone for ParValues<'_, K, V, S> {
116 #[cfg_attr(feature = "inline-more", inline)]
117 fn clone(&self) -> Self {
118 ParValues { map: self.map }
119 }
120 }
121
122 impl<K: Eq + Hash, V: fmt::Debug, S: BuildHasher> fmt::Debug for ParValues<'_, K, V, S> {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 self.map.values().fmt(f)
125 }
126 }
127
128 /// Parallel iterator over mutable references to entries in a map.
129 ///
130 /// This iterator is created by the [`par_iter_mut`] method on [`HashMap`]
131 /// (provided by the [`IntoParallelRefMutIterator`] trait).
132 /// See its documentation for more.
133 ///
134 /// [`par_iter_mut`]: /hashbrown/struct.HashMap.html#method.par_iter_mut
135 /// [`HashMap`]: /hashbrown/struct.HashMap.html
136 /// [`IntoParallelRefMutIterator`]: https://docs.rs/rayon/1.0/rayon/iter/trait.IntoParallelRefMutIterator.html
137 pub struct ParIterMut<'a, K, V, S> {
138 map: &'a mut HashMap<K, V, S>,
139 }
140
141 impl<'a, K: Send + Sync, V: Send, S: Send> ParallelIterator for ParIterMut<'a, K, V, S> {
142 type Item = (&'a K, &'a mut V);
143
144 #[cfg_attr(feature = "inline-more", inline)]
145 fn drive_unindexed<C>(self, consumer: C) -> C::Result
146 where
147 C: UnindexedConsumer<Self::Item>,
148 {
149 unsafe { self.map.table.par_iter() }
150 .map(|x| unsafe {
151 let r = x.as_mut();
152 (&r.0, &mut r.1)
153 })
154 .drive_unindexed(consumer)
155 }
156 }
157
158 impl<K: fmt::Debug + Eq + Hash, V: fmt::Debug, S: BuildHasher> fmt::Debug
159 for ParIterMut<'_, K, V, S>
160 {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 self.map.iter().fmt(f)
163 }
164 }
165
166 /// Parallel iterator over mutable references to values in a map.
167 ///
168 /// This iterator is created by the [`par_values_mut`] method on [`HashMap`].
169 /// See its documentation for more.
170 ///
171 /// [`par_values_mut`]: /hashbrown/struct.HashMap.html#method.par_values_mut
172 /// [`HashMap`]: /hashbrown/struct.HashMap.html
173 pub struct ParValuesMut<'a, K, V, S> {
174 map: &'a mut HashMap<K, V, S>,
175 }
176
177 impl<'a, K: Send, V: Send, S: Send> ParallelIterator for ParValuesMut<'a, K, V, S> {
178 type Item = &'a mut V;
179
180 #[cfg_attr(feature = "inline-more", inline)]
181 fn drive_unindexed<C>(self, consumer: C) -> C::Result
182 where
183 C: UnindexedConsumer<Self::Item>,
184 {
185 unsafe { self.map.table.par_iter() }
186 .map(|x| unsafe { &mut x.as_mut().1 })
187 .drive_unindexed(consumer)
188 }
189 }
190
191 impl<K: Eq + Hash, V: fmt::Debug, S: BuildHasher> fmt::Debug for ParValuesMut<'_, K, V, S> {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 self.map.values().fmt(f)
194 }
195 }
196
197 /// Parallel iterator over entries of a consumed map.
198 ///
199 /// This iterator is created by the [`into_par_iter`] method on [`HashMap`]
200 /// (provided by the [`IntoParallelIterator`] trait).
201 /// See its documentation for more.
202 ///
203 /// [`into_par_iter`]: /hashbrown/struct.HashMap.html#method.into_par_iter
204 /// [`HashMap`]: /hashbrown/struct.HashMap.html
205 /// [`IntoParallelIterator`]: https://docs.rs/rayon/1.0/rayon/iter/trait.IntoParallelIterator.html
206 pub struct IntoParIter<K, V, S> {
207 map: HashMap<K, V, S>,
208 }
209
210 impl<K: Send, V: Send, S: Send> ParallelIterator for IntoParIter<K, V, S> {
211 type Item = (K, V);
212
213 #[cfg_attr(feature = "inline-more", inline)]
214 fn drive_unindexed<C>(self, consumer: C) -> C::Result
215 where
216 C: UnindexedConsumer<Self::Item>,
217 {
218 self.map.table.into_par_iter().drive_unindexed(consumer)
219 }
220 }
221
222 impl<K: fmt::Debug + Eq + Hash, V: fmt::Debug, S: BuildHasher> fmt::Debug for IntoParIter<K, V, S> {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 self.map.iter().fmt(f)
225 }
226 }
227
228 /// Parallel draining iterator over entries of a map.
229 ///
230 /// This iterator is created by the [`par_drain`] method on [`HashMap`].
231 /// See its documentation for more.
232 ///
233 /// [`par_drain`]: /hashbrown/struct.HashMap.html#method.par_drain
234 /// [`HashMap`]: /hashbrown/struct.HashMap.html
235 pub struct ParDrain<'a, K, V, S> {
236 map: &'a mut HashMap<K, V, S>,
237 }
238
239 impl<K: Send, V: Send, S: Send> ParallelIterator for ParDrain<'_, K, V, S> {
240 type Item = (K, V);
241
242 #[cfg_attr(feature = "inline-more", inline)]
243 fn drive_unindexed<C>(self, consumer: C) -> C::Result
244 where
245 C: UnindexedConsumer<Self::Item>,
246 {
247 self.map.table.par_drain().drive_unindexed(consumer)
248 }
249 }
250
251 impl<K: fmt::Debug + Eq + Hash, V: fmt::Debug, S: BuildHasher> fmt::Debug
252 for ParDrain<'_, K, V, S>
253 {
254 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255 self.map.iter().fmt(f)
256 }
257 }
258
259 impl<K: Sync, V: Sync, S: Sync> HashMap<K, V, S> {
260 /// Visits (potentially in parallel) immutably borrowed keys in an arbitrary order.
261 #[cfg_attr(feature = "inline-more", inline)]
262 pub fn par_keys(&self) -> ParKeys<'_, K, V, S> {
263 ParKeys { map: self }
264 }
265
266 /// Visits (potentially in parallel) immutably borrowed values in an arbitrary order.
267 #[cfg_attr(feature = "inline-more", inline)]
268 pub fn par_values(&self) -> ParValues<'_, K, V, S> {
269 ParValues { map: self }
270 }
271 }
272
273 impl<K: Send, V: Send, S: Send> HashMap<K, V, S> {
274 /// Visits (potentially in parallel) mutably borrowed values in an arbitrary order.
275 #[cfg_attr(feature = "inline-more", inline)]
276 pub fn par_values_mut(&mut self) -> ParValuesMut<'_, K, V, S> {
277 ParValuesMut { map: self }
278 }
279
280 /// Consumes (potentially in parallel) all values in an arbitrary order,
281 /// while preserving the map's allocated memory for reuse.
282 #[cfg_attr(feature = "inline-more", inline)]
283 pub fn par_drain(&mut self) -> ParDrain<'_, K, V, S> {
284 ParDrain { map: self }
285 }
286 }
287
288 impl<K, V, S> HashMap<K, V, S>
289 where
290 K: Eq + Hash + Sync,
291 V: PartialEq + Sync,
292 S: BuildHasher + Sync,
293 {
294 /// Returns `true` if the map is equal to another,
295 /// i.e. both maps contain the same keys mapped to the same values.
296 ///
297 /// This method runs in a potentially parallel fashion.
298 pub fn par_eq(&self, other: &Self) -> bool {
299 self.len() == other.len()
300 && self
301 .into_par_iter()
302 .all(|(key, value)| other.get(key).map_or(false, |v| *value == *v))
303 }
304 }
305
306 impl<K: Send, V: Send, S: Send> IntoParallelIterator for HashMap<K, V, S> {
307 type Item = (K, V);
308 type Iter = IntoParIter<K, V, S>;
309
310 #[cfg_attr(feature = "inline-more", inline)]
311 fn into_par_iter(self) -> Self::Iter {
312 IntoParIter { map: self }
313 }
314 }
315
316 impl<'a, K: Sync, V: Sync, S: Sync> IntoParallelIterator for &'a HashMap<K, V, S> {
317 type Item = (&'a K, &'a V);
318 type Iter = ParIter<'a, K, V, S>;
319
320 #[cfg_attr(feature = "inline-more", inline)]
321 fn into_par_iter(self) -> Self::Iter {
322 ParIter { map: self }
323 }
324 }
325
326 impl<'a, K: Send + Sync, V: Send, S: Send> IntoParallelIterator for &'a mut HashMap<K, V, S> {
327 type Item = (&'a K, &'a mut V);
328 type Iter = ParIterMut<'a, K, V, S>;
329
330 #[cfg_attr(feature = "inline-more", inline)]
331 fn into_par_iter(self) -> Self::Iter {
332 ParIterMut { map: self }
333 }
334 }
335
336 /// Collect (key, value) pairs from a parallel iterator into a
337 /// hashmap. If multiple pairs correspond to the same key, then the
338 /// ones produced earlier in the parallel iterator will be
339 /// overwritten, just as with a sequential iterator.
340 impl<K, V, S> FromParallelIterator<(K, V)> for HashMap<K, V, S>
341 where
342 K: Eq + Hash + Send,
343 V: Send,
344 S: BuildHasher + Default,
345 {
346 fn from_par_iter<P>(par_iter: P) -> Self
347 where
348 P: IntoParallelIterator<Item = (K, V)>,
349 {
350 let mut map = HashMap::default();
351 map.par_extend(par_iter);
352 map
353 }
354 }
355
356 /// Extend a hash map with items from a parallel iterator.
357 impl<K, V, S> ParallelExtend<(K, V)> for HashMap<K, V, S>
358 where
359 K: Eq + Hash + Send,
360 V: Send,
361 S: BuildHasher,
362 {
363 fn par_extend<I>(&mut self, par_iter: I)
364 where
365 I: IntoParallelIterator<Item = (K, V)>,
366 {
367 extend(self, par_iter);
368 }
369 }
370
371 /// Extend a hash map with copied items from a parallel iterator.
372 impl<'a, K, V, S> ParallelExtend<(&'a K, &'a V)> for HashMap<K, V, S>
373 where
374 K: Copy + Eq + Hash + Sync,
375 V: Copy + Sync,
376 S: BuildHasher,
377 {
378 fn par_extend<I>(&mut self, par_iter: I)
379 where
380 I: IntoParallelIterator<Item = (&'a K, &'a V)>,
381 {
382 extend(self, par_iter);
383 }
384 }
385
386 // This is equal to the normal `HashMap` -- no custom advantage.
387 fn extend<K, V, S, I>(map: &mut HashMap<K, V, S>, par_iter: I)
388 where
389 K: Eq + Hash,
390 S: BuildHasher,
391 I: IntoParallelIterator,
392 HashMap<K, V, S>: Extend<I::Item>,
393 {
394 let (list, len) = super::helpers::collect(par_iter);
395
396 // Keys may be already present or show multiple times in the iterator.
397 // Reserve the entire length if the map is empty.
398 // Otherwise reserve half the length (rounded up), so the map
399 // will only resize twice in the worst case.
400 let reserve = if map.is_empty() { len } else { (len + 1) / 2 };
401 map.reserve(reserve);
402 for vec in list {
403 map.extend(vec);
404 }
405 }
406
407 #[cfg(test)]
408 mod test_par_map {
409 use alloc::vec::Vec;
410 use core::hash::{Hash, Hasher};
411 use core::sync::atomic::{AtomicUsize, Ordering};
412
413 use rayon::prelude::*;
414
415 use crate::hash_map::HashMap;
416
417 struct Dropable<'a> {
418 k: usize,
419 counter: &'a AtomicUsize,
420 }
421
422 impl Dropable<'_> {
423 fn new(k: usize, counter: &AtomicUsize) -> Dropable<'_> {
424 counter.fetch_add(1, Ordering::Relaxed);
425
426 Dropable { k, counter }
427 }
428 }
429
430 impl Drop for Dropable<'_> {
431 fn drop(&mut self) {
432 self.counter.fetch_sub(1, Ordering::Relaxed);
433 }
434 }
435
436 impl Clone for Dropable<'_> {
437 fn clone(&self) -> Self {
438 Dropable::new(self.k, self.counter)
439 }
440 }
441
442 impl Hash for Dropable<'_> {
443 fn hash<H>(&self, state: &mut H)
444 where
445 H: Hasher,
446 {
447 self.k.hash(state)
448 }
449 }
450
451 impl PartialEq for Dropable<'_> {
452 fn eq(&self, other: &Self) -> bool {
453 self.k == other.k
454 }
455 }
456
457 impl Eq for Dropable<'_> {}
458
459 #[test]
460 fn test_into_iter_drops() {
461 let key = AtomicUsize::new(0);
462 let value = AtomicUsize::new(0);
463
464 let hm = {
465 let mut hm = HashMap::new();
466
467 assert_eq!(key.load(Ordering::Relaxed), 0);
468 assert_eq!(value.load(Ordering::Relaxed), 0);
469
470 for i in 0..100 {
471 let d1 = Dropable::new(i, &key);
472 let d2 = Dropable::new(i + 100, &value);
473 hm.insert(d1, d2);
474 }
475
476 assert_eq!(key.load(Ordering::Relaxed), 100);
477 assert_eq!(value.load(Ordering::Relaxed), 100);
478
479 hm
480 };
481
482 // By the way, ensure that cloning doesn't screw up the dropping.
483 drop(hm.clone());
484
485 assert_eq!(key.load(Ordering::Relaxed), 100);
486 assert_eq!(value.load(Ordering::Relaxed), 100);
487
488 // Ensure that dropping the iterator does not leak anything.
489 drop(hm.clone().into_par_iter());
490
491 {
492 assert_eq!(key.load(Ordering::Relaxed), 100);
493 assert_eq!(value.load(Ordering::Relaxed), 100);
494
495 // retain only half
496 let _v: Vec<_> = hm
497 .into_par_iter()
498 .filter(|&(ref key, _)| key.k < 50)
499 .collect();
500
501 assert_eq!(key.load(Ordering::Relaxed), 50);
502 assert_eq!(value.load(Ordering::Relaxed), 50);
503 };
504
505 assert_eq!(key.load(Ordering::Relaxed), 0);
506 assert_eq!(value.load(Ordering::Relaxed), 0);
507 }
508
509 #[test]
510 fn test_drain_drops() {
511 let key = AtomicUsize::new(0);
512 let value = AtomicUsize::new(0);
513
514 let mut hm = {
515 let mut hm = HashMap::new();
516
517 assert_eq!(key.load(Ordering::Relaxed), 0);
518 assert_eq!(value.load(Ordering::Relaxed), 0);
519
520 for i in 0..100 {
521 let d1 = Dropable::new(i, &key);
522 let d2 = Dropable::new(i + 100, &value);
523 hm.insert(d1, d2);
524 }
525
526 assert_eq!(key.load(Ordering::Relaxed), 100);
527 assert_eq!(value.load(Ordering::Relaxed), 100);
528
529 hm
530 };
531
532 // By the way, ensure that cloning doesn't screw up the dropping.
533 drop(hm.clone());
534
535 assert_eq!(key.load(Ordering::Relaxed), 100);
536 assert_eq!(value.load(Ordering::Relaxed), 100);
537
538 // Ensure that dropping the drain iterator does not leak anything.
539 drop(hm.clone().par_drain());
540
541 {
542 assert_eq!(key.load(Ordering::Relaxed), 100);
543 assert_eq!(value.load(Ordering::Relaxed), 100);
544
545 // retain only half
546 let _v: Vec<_> = hm.drain().filter(|&(ref key, _)| key.k < 50).collect();
547 assert!(hm.is_empty());
548
549 assert_eq!(key.load(Ordering::Relaxed), 50);
550 assert_eq!(value.load(Ordering::Relaxed), 50);
551 };
552
553 assert_eq!(key.load(Ordering::Relaxed), 0);
554 assert_eq!(value.load(Ordering::Relaxed), 0);
555 }
556
557 #[test]
558 fn test_empty_iter() {
559 let mut m: HashMap<isize, bool> = HashMap::new();
560 assert_eq!(m.par_drain().count(), 0);
561 assert_eq!(m.par_keys().count(), 0);
562 assert_eq!(m.par_values().count(), 0);
563 assert_eq!(m.par_values_mut().count(), 0);
564 assert_eq!(m.par_iter().count(), 0);
565 assert_eq!(m.par_iter_mut().count(), 0);
566 assert_eq!(m.len(), 0);
567 assert!(m.is_empty());
568 assert_eq!(m.into_par_iter().count(), 0);
569 }
570
571 #[test]
572 fn test_iterate() {
573 let mut m = HashMap::with_capacity(4);
574 for i in 0..32 {
575 assert!(m.insert(i, i * 2).is_none());
576 }
577 assert_eq!(m.len(), 32);
578
579 let observed = AtomicUsize::new(0);
580
581 m.par_iter().for_each(|(k, v)| {
582 assert_eq!(*v, *k * 2);
583 observed.fetch_or(1 << *k, Ordering::Relaxed);
584 });
585 assert_eq!(observed.into_inner(), 0xFFFF_FFFF);
586 }
587
588 #[test]
589 fn test_keys() {
590 let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
591 let map: HashMap<_, _> = vec.into_par_iter().collect();
592 let keys: Vec<_> = map.par_keys().cloned().collect();
593 assert_eq!(keys.len(), 3);
594 assert!(keys.contains(&1));
595 assert!(keys.contains(&2));
596 assert!(keys.contains(&3));
597 }
598
599 #[test]
600 fn test_values() {
601 let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
602 let map: HashMap<_, _> = vec.into_par_iter().collect();
603 let values: Vec<_> = map.par_values().cloned().collect();
604 assert_eq!(values.len(), 3);
605 assert!(values.contains(&'a'));
606 assert!(values.contains(&'b'));
607 assert!(values.contains(&'c'));
608 }
609
610 #[test]
611 fn test_values_mut() {
612 let vec = vec![(1, 1), (2, 2), (3, 3)];
613 let mut map: HashMap<_, _> = vec.into_par_iter().collect();
614 map.par_values_mut().for_each(|value| *value = (*value) * 2);
615 let values: Vec<_> = map.par_values().cloned().collect();
616 assert_eq!(values.len(), 3);
617 assert!(values.contains(&2));
618 assert!(values.contains(&4));
619 assert!(values.contains(&6));
620 }
621
622 #[test]
623 fn test_eq() {
624 let mut m1 = HashMap::new();
625 m1.insert(1, 2);
626 m1.insert(2, 3);
627 m1.insert(3, 4);
628
629 let mut m2 = HashMap::new();
630 m2.insert(1, 2);
631 m2.insert(2, 3);
632
633 assert!(!m1.par_eq(&m2));
634
635 m2.insert(3, 4);
636
637 assert!(m1.par_eq(&m2));
638 }
639
640 #[test]
641 fn test_from_iter() {
642 let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
643
644 let map: HashMap<_, _> = xs.par_iter().cloned().collect();
645
646 for &(k, v) in &xs {
647 assert_eq!(map.get(&k), Some(&v));
648 }
649 }
650
651 #[test]
652 fn test_extend_ref() {
653 let mut a = HashMap::new();
654 a.insert(1, "one");
655 let mut b = HashMap::new();
656 b.insert(2, "two");
657 b.insert(3, "three");
658
659 a.par_extend(&b);
660
661 assert_eq!(a.len(), 3);
662 assert_eq!(a[&1], "one");
663 assert_eq!(a[&2], "two");
664 assert_eq!(a[&3], "three");
665 }
666 }