diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 97bb21c8a36e8..fd4a2af95a55c 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -461,182 +461,165 @@ impl Extend<()> for () { macro_rules! spec_tuple_impl { ( + [$($params_:tt)*], ( - $ty_name:ident, $var_name:ident, $extend_ty_name: ident, - $trait_name:ident, $default_fn_name:ident, $cnt:tt + $params:tt, $SpecTupleExtendN:tt, $default_extend_tuple_n:tt ), + $($remainder:tt,)* ) => { spec_tuple_impl!( - $trait_name, - $default_fn_name, + $SpecTupleExtendN, + $default_extend_tuple_n, + $($params_,)* + $params + ); + spec_tuple_impl!( + [$($params_)* $params], + $($remainder,)* + ); + }; + ( [$($params_:tt)*], ) => { }; + ( + $SpecTupleExtendN:tt, $default_extend_tuple_n:tt, + $params:tt + ) => { + spec_tuple_impl!( + $SpecTupleExtendN, + $default_extend_tuple_n, + #special_case_1_tuple #[doc(fake_variadic)] #[doc = "This trait is implemented for tuples up to twelve items long. The `impl`s for \ 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in \ 1.85.0."] - => ($ty_name, $var_name, $extend_ty_name, $cnt), + => $params, ); }; ( - ( - $ty_name:ident, $var_name:ident, $extend_ty_name: ident, - $trait_name:ident, $default_fn_name:ident, $cnt:tt - ), - $( - ( - $ty_names:ident, $var_names:ident, $extend_ty_names:ident, - $trait_names:ident, $default_fn_names:ident, $cnts:tt - ), - )* + $SpecTupleExtendN:tt, $default_extend_tuple_n:tt, + $($params:tt),+ ) => { spec_tuple_impl!( - $( - ( - $ty_names, $var_names, $extend_ty_names, - $trait_names, $default_fn_names, $cnts - ), - )* - ); - spec_tuple_impl!( - $trait_name, - $default_fn_name, + $SpecTupleExtendN, + $default_extend_tuple_n, + #normal_case #[doc(hidden)] - => ( - $ty_name, $var_name, $extend_ty_name, $cnt - ), - $( - ( - $ty_names, $var_names, $extend_ty_names, $cnts - ), - )* + => $($params,)+ ); }; ( - $trait_name:ident, $default_fn_name:ident, #[$meta:meta] + $SpecTupleExtendN:tt, $default_extend_tuple_n:tt, + #$skip_specialization_trait_for_1_tuple:tt + #[$meta:meta] $(#[$doctext:meta])? => $( ( - $ty_names:ident, $var_names:ident, $extend_ty_names:ident, $cnts:tt + $T:ident, $var_names:ident, $ExtendT:ident, $cnt:tt ), )* ) => { #[$meta] $(#[$doctext])? #[stable(feature = "extend_for_tuple", since = "1.56.0")] - impl<$($ty_names,)* $($extend_ty_names,)*> Extend<($($ty_names,)*)> for ($($extend_ty_names,)*) + impl<$($T,)* $($ExtendT,)*> Extend<($($T,)*)> for ($($ExtendT,)*) where - $($extend_ty_names: Extend<$ty_names>,)* + $($ExtendT: Extend<$T>,)* { - /// Allows to `extend` a tuple of collections that also implement `Extend`. - /// - /// See also: [`Iterator::unzip`] - /// - /// # Examples - /// ``` - /// // Example given for a 2-tuple, but 1- through 12-tuples are supported - /// let mut tuple = (vec![0], vec![1]); - /// tuple.extend([(2, 3), (4, 5), (6, 7)]); - /// assert_eq!(tuple.0, [0, 2, 4, 6]); - /// assert_eq!(tuple.1, [1, 3, 5, 7]); - /// - /// // also allows for arbitrarily nested tuples as elements - /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); - /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); - /// - /// let (a, (b, c)) = nested_tuple; - /// assert_eq!(a, [1, 4, 7]); - /// assert_eq!(b, [2, 5, 8]); - /// assert_eq!(c, [3, 6, 9]); - /// ``` - fn extend>(&mut self, into_iter: T) { - let ($($var_names,)*) = self; - let iter = into_iter.into_iter(); - $trait_name::extend(iter, $($var_names,)*); - } + spec_tuple_impl!( + #$skip_specialization_trait_for_1_tuple - fn extend_one(&mut self, item: ($($ty_names,)*)) { - $(self.$cnts.extend_one(item.$cnts);)* + fn extend>(&mut self, into_iter: I) { + let ($($var_names,)*) = self; + let iter = into_iter.into_iter(); + $SpecTupleExtendN::extend(iter, $($var_names,)*); + } + ); + + fn extend_one(&mut self, item: ($($T,)*)) { + $(self.$cnt.extend_one(item.$cnt);)* } fn extend_reserve(&mut self, additional: usize) { - $(self.$cnts.extend_reserve(additional);)* + $(self.$cnt.extend_reserve(additional);)* } - unsafe fn extend_one_unchecked(&mut self, item: ($($ty_names,)*)) { + unsafe fn extend_one_unchecked(&mut self, item: ($($T,)*)) { // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. unsafe { - $(self.$cnts.extend_one_unchecked(item.$cnts);)* + $(self.$cnt.extend_one_unchecked(item.$cnt);)* } } } - trait $trait_name<$($ty_names),*> { - fn extend(self, $($var_names: &mut $ty_names,)*); - } + spec_tuple_impl!( + #$skip_specialization_trait_for_1_tuple - fn $default_fn_name<$($ty_names,)* $($extend_ty_names,)*>( - iter: impl Iterator, - $($var_names: &mut $extend_ty_names,)* - ) where - $($extend_ty_names: Extend<$ty_names>,)* - { - fn extend<'a, $($ty_names,)*>( - $($var_names: &'a mut impl Extend<$ty_names>,)* - ) -> impl FnMut((), ($($ty_names,)*)) + 'a { - #[allow(non_snake_case)] - move |(), ($($extend_ty_names,)*)| { - $($var_names.extend_one($extend_ty_names);)* - } + trait $SpecTupleExtendN<$($T),*> { + fn extend(self, $($var_names: &mut $T,)*); } - let (lower_bound, _) = iter.size_hint(); - if lower_bound > 0 { - $($var_names.extend_reserve(lower_bound);)* - } + fn $default_extend_tuple_n<$($T,)* $($ExtendT,)*>( + iter: impl Iterator, + $($var_names: &mut $ExtendT,)* + ) where + $($ExtendT: Extend<$T>,)* + { + fn extend<'a, $($T,)*>( + $($var_names: &'a mut impl Extend<$T>,)* + ) -> impl FnMut((), ($($T,)*)) + 'a { + move |(), item| { + $($var_names.extend_one(item.$cnt);)* + } + } - iter.fold((), extend($($var_names,)*)); - } + let (lower_bound, _) = iter.size_hint(); + if lower_bound > 0 { + $($var_names.extend_reserve(lower_bound);)* + } - impl<$($ty_names,)* $($extend_ty_names,)* Iter> $trait_name<$($extend_ty_names),*> for Iter - where - $($extend_ty_names: Extend<$ty_names>,)* - Iter: Iterator, - { - default fn extend(self, $($var_names: &mut $extend_ty_names),*) { - $default_fn_name(self, $($var_names),*); + iter.fold((), extend($($var_names,)*)); } - } - impl<$($ty_names,)* $($extend_ty_names,)* Iter> $trait_name<$($extend_ty_names),*> for Iter - where - $($extend_ty_names: Extend<$ty_names>,)* - Iter: TrustedLen, - { - fn extend(self, $($var_names: &mut $extend_ty_names,)*) { - fn extend<'a, $($ty_names,)*>( - $($var_names: &'a mut impl Extend<$ty_names>,)* - ) -> impl FnMut((), ($($ty_names,)*)) + 'a { - #[allow(non_snake_case)] - // SAFETY: We reserve enough space for the `size_hint`, and the iterator is - // `TrustedLen` so its `size_hint` is exact. - move |(), ($($extend_ty_names,)*)| unsafe { - $($var_names.extend_one_unchecked($extend_ty_names);)* - } + impl<$($T,)* $($ExtendT,)* Iter> $SpecTupleExtendN<$($ExtendT),*> for Iter + where + $($ExtendT: Extend<$T>,)* + Iter: Iterator, + { + default fn extend(self, $($var_names: &mut $ExtendT),*) { + $default_extend_tuple_n(self, $($var_names),*); } + } - let (lower_bound, upper_bound) = self.size_hint(); + impl<$($T,)* $($ExtendT,)* Iter> $SpecTupleExtendN<$($ExtendT),*> for Iter + where + $($ExtendT: Extend<$T>,)* + Iter: TrustedLen, + { + fn extend(self, $($var_names: &mut $ExtendT,)*) { + fn extend<'a, $($T,)*>( + $($var_names: &'a mut impl Extend<$T>,)* + ) -> impl FnMut((), ($($T,)*)) + 'a { + // SAFETY: We reserve enough space for the `size_hint`, and the iterator is + // `TrustedLen` so its `size_hint` is exact. + move |(), item| unsafe { + $($var_names.extend_one_unchecked(item.$cnt);)* + } + } - if upper_bound.is_none() { - // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. - $default_fn_name(self, $($var_names,)*); - return; - } + let (lower_bound, upper_bound) = self.size_hint(); - if lower_bound > 0 { - $($var_names.extend_reserve(lower_bound);)* - } + if upper_bound.is_none() { + // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. + $default_extend_tuple_n(self, $($var_names,)*); + return; + } + + if lower_bound > 0 { + $($var_names.extend_reserve(lower_bound);)* + } - self.fold((), extend($($var_names,)*)); + self.fold((), extend($($var_names,)*)); + } } - } + ); /// This implementation turns an iterator of tuples into a tuple of types which implement /// [`Default`] and [`Extend`]. @@ -661,32 +644,73 @@ macro_rules! spec_tuple_impl { #[$meta] $(#[$doctext])? #[stable(feature = "from_iterator_for_tuple", since = "1.79.0")] - impl<$($ty_names,)* $($extend_ty_names,)*> FromIterator<($($extend_ty_names,)*)> for ($($ty_names,)*) + impl<$($T,)* $($ExtendT,)*> FromIterator<($($ExtendT,)*)> for ($($T,)*) where - $($ty_names: Default + Extend<$extend_ty_names>,)* + $($T: Default + Extend<$ExtendT>,)* { - fn from_iter>(iter: Iter) -> Self { - let mut res = <($($ty_names,)*)>::default(); + fn from_iter>(iter: Iter) -> Self { + let mut res = <($($T,)*)>::default(); res.extend(iter); res } } - + }; + ( + #special_case_1_tuple + fn $($t:tt)* + ) => { + /// Allows to `extend` a tuple of collections that also implement `Extend`. + /// + /// See also: [`Iterator::unzip`] + /// + /// # Examples + /// ``` + /// // Example given for a 2-tuple, but 1- through 12-tuples are supported + /// let mut tuple = (vec![0], vec![1]); + /// tuple.extend([(2, 3), (4, 5), (6, 7)]); + /// assert_eq!(tuple.0, [0, 2, 4, 6]); + /// assert_eq!(tuple.1, [1, 3, 5, 7]); + /// + /// // also allows for arbitrarily nested tuples as elements + /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); + /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); + /// + /// let (a, (b, c)) = nested_tuple; + /// assert_eq!(a, [1, 4, 7]); + /// assert_eq!(b, [2, 5, 8]); + /// assert_eq!(c, [3, 6, 9]); + /// ``` + fn extend>(&mut self, iter: I) { + self.0.extend(iter.into_iter().map(|(a,)| a)); + } + }; + ( + #special_case_1_tuple + $($t:tt)* + ) => { + // no specialization traits + }; + ( + #normal_case + $($t:tt)* + ) => { + $($t)* }; } spec_tuple_impl!( - (L, l, EL, TraitL, default_extend_tuple_l, 11), - (K, k, EK, TraitK, default_extend_tuple_k, 10), - (J, j, EJ, TraitJ, default_extend_tuple_j, 9), - (I, i, EI, TraitI, default_extend_tuple_i, 8), - (H, h, EH, TraitH, default_extend_tuple_h, 7), - (G, g, EG, TraitG, default_extend_tuple_g, 6), - (F, f, EF, TraitF, default_extend_tuple_f, 5), - (E, e, EE, TraitE, default_extend_tuple_e, 4), - (D, d, ED, TraitD, default_extend_tuple_d, 3), - (C, c, EC, TraitC, default_extend_tuple_c, 2), - (B, b, EB, TraitB, default_extend_tuple_b, 1), - (A, a, EA, TraitA, default_extend_tuple_a, 0), + [], + ((T, t, ExtendT, 0), @, @), + ((U, u, ExtendU, 1), SpecTupleExtend2, default_extend_tuple_2), + ((V, v, ExtendV, 2), SpecTupleExtend3, default_extend_tuple_3), + ((W, w, ExtendW, 3), SpecTupleExtend4, default_extend_tuple_4), + ((X, x, ExtendX, 4), SpecTupleExtend5, default_extend_tuple_5), + ((Y, y, ExtendY, 5), SpecTupleExtend6, default_extend_tuple_6), + ((Z, z, ExtendZ, 6), SpecTupleExtend7, default_extend_tuple_7), + ((A, a, ExtendA, 7), SpecTupleExtend8, default_extend_tuple_8), + ((B, b, ExtendB, 8), SpecTupleExtend9, default_extend_tuple_9), + ((C, c, ExtendC, 9), SpecTupleExtend10, default_extend_tuple_10), + ((D, d, ExtendD, 10), SpecTupleExtend11, default_extend_tuple_11), + ((E, e, ExtendE, 11), SpecTupleExtend12, default_extend_tuple_12), ); diff --git a/library/coretests/tests/iter/traits/iterator.rs b/library/coretests/tests/iter/traits/iterator.rs index e31d2e15b6d7e..5ef1f797ae55d 100644 --- a/library/coretests/tests/iter/traits/iterator.rs +++ b/library/coretests/tests/iter/traits/iterator.rs @@ -1,3 +1,5 @@ +use core::cell::RefCell; +use core::iter::zip; use core::num::NonZero; /// A wrapper struct that implements `Eq` and `Ord` based on the wrapped @@ -642,6 +644,26 @@ fn test_collect_for_tuples() { assert!(e.2 == d); } +#[test] +fn test_extend_for_tuple_side_effects_order() { + struct TrackingExtender<'a, T>(&'static str, &'a RefCell)>>, Vec); + impl Extend for TrackingExtender<'_, T> { + fn extend>(&mut self, i: I) { + let items = Vec::from_iter(i); + self.1.borrow_mut().push((self.0, items.clone())); + self.2.extend(items); + } + } + + let effects = RefCell::new(vec![]); + let l = TrackingExtender("l", &effects, vec![]); + let r = TrackingExtender("r", &effects, vec![]); + let mut p = ((l, r), ()); + p.extend(zip([(1, 2), (3, 4)], [(), ()])); + let effects = effects.into_inner(); + assert_eq!(effects, [("l", vec![1]), ("r", vec![2]), ("l", vec![3]), ("r", vec![4])]); +} + // just tests by whether or not this compiles fn _empty_impl_all_auto_traits() { use std::panic::{RefUnwindSafe, UnwindSafe};