Skip to content

Commit b6a546c

Browse files
committed
Impl FromIterator for tuples with arity 1-12
1 parent 27342cb commit b6a546c

File tree

2 files changed

+68
-21
lines changed

2 files changed

+68
-21
lines changed

library/core/src/iter/traits/collect.rs

+56-21
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ pub trait FromIterator<A>: Sized {
152152
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self;
153153
}
154154

155-
/// This implementation turns an iterator of tuples into a tuple of types which implement
155+
/*/// This implementation turns an iterator of tuples into a tuple of types which implement
156156
/// [`Default`] and [`Extend`].
157157
///
158158
/// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`]
@@ -183,7 +183,7 @@ where
183183
184184
res
185185
}
186-
}
186+
}*/
187187

188188
/// Conversion into an [`Iterator`].
189189
///
@@ -595,32 +595,67 @@ macro_rules! spec_tuple_impl {
595595
Iter: TrustedLen<Item = ($($ty_names,)*)>,
596596
{
597597
fn extend(self, $($var_names: &mut $extend_ty_names,)*) {
598-
fn extend<'a, $($ty_names,)*>(
599-
$($var_names: &'a mut impl Extend<$ty_names>,)*
600-
) -> impl FnMut((), ($($ty_names,)*)) + 'a {
601-
#[allow(non_snake_case)]
602-
// SAFETY: We reserve enough space for the `size_hint`, and the iterator is `TrustedLen`
603-
// so its `size_hint` is exact.
604-
move |(), ($($extend_ty_names,)*)| unsafe {
605-
$($var_names.extend_one_unchecked($extend_ty_names);)*
598+
fn extend<'a, $($ty_names,)*>(
599+
$($var_names: &'a mut impl Extend<$ty_names>,)*
600+
) -> impl FnMut((), ($($ty_names,)*)) + 'a {
601+
#[allow(non_snake_case)]
602+
// SAFETY: We reserve enough space for the `size_hint`, and the iterator is `TrustedLen`
603+
// so its `size_hint` is exact.
604+
move |(), ($($extend_ty_names,)*)| unsafe {
605+
$($var_names.extend_one_unchecked($extend_ty_names);)*
606+
}
606607
}
607-
}
608608

609-
let (lower_bound, upper_bound) = self.size_hint();
609+
let (lower_bound, upper_bound) = self.size_hint();
610610

611-
if upper_bound.is_none() {
612-
// We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway.
613-
$default_fn_name(self, $($var_names,)*);
614-
return;
615-
}
611+
if upper_bound.is_none() {
612+
// We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway.
613+
$default_fn_name(self, $($var_names,)*);
614+
return;
615+
}
616616

617-
if lower_bound > 0 {
618-
$($var_names.extend_reserve(lower_bound);)*
617+
if lower_bound > 0 {
618+
$($var_names.extend_reserve(lower_bound);)*
619+
}
620+
621+
self.fold((), extend($($var_names,)*));
619622
}
623+
}
624+
625+
/// This implementation turns an iterator of tuples into a tuple of types which implement
626+
/// [`Default`] and [`Extend`].
627+
///
628+
/// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`]
629+
/// implementations:
630+
///
631+
/// ```rust
632+
/// # fn main() -> Result<(), core::num::ParseIntError> {
633+
/// let string = "1,2,123,4";
634+
///
635+
/// // Example given for a 2-tuple, but 1- through 12-tuples are supported
636+
/// let (numbers, lengths): (Vec<_>, Vec<_>) = string
637+
/// .split(',')
638+
/// .map(|s| s.parse().map(|n: u32| (n, s.len())))
639+
/// .collect::<Result<_, _>>()?;
640+
///
641+
/// assert_eq!(numbers, [1, 2, 123, 4]);
642+
/// assert_eq!(lengths, [1, 1, 3, 1]);
643+
/// # Ok(()) }
644+
/// ```
645+
#[$meta]
646+
$(#[$doctext])?
647+
#[stable(feature = "from_iterator_for_tuple", since = "1.79.0")]
648+
impl<$($ty_names,)* $($extend_ty_names,)*> FromIterator<($($extend_ty_names,)*)> for ($($ty_names,)*)
649+
where
650+
$($ty_names: Default + Extend<$extend_ty_names>,)*
651+
{
652+
fn from_iter<Iter: IntoIterator<Item = ($($extend_ty_names,)*)>>(iter: Iter) -> Self {
653+
let mut res = <($($ty_names,)*)>::default();
654+
res.extend(iter);
620655

621-
self.fold((), extend($($var_names,)*));
656+
res
657+
}
622658
}
623-
}
624659

625660
};
626661
}

library/core/tests/iter/traits/iterator.rs

+12
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,18 @@ fn test_collect_into_tuples() {
630630
assert!(e.2 == d);
631631
}
632632

633+
#[test]
634+
fn test_collect_for_tuples() {
635+
let a = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)];
636+
let b = vec![1, 4, 7];
637+
let c = vec![2, 5, 8];
638+
let d = vec![3, 6, 9];
639+
let e: (Vec<_>, Vec<_>, Vec<_>) = a.into_iter().collect();
640+
assert!(e.0 == b);
641+
assert!(e.1 == c);
642+
assert!(e.2 == d);
643+
}
644+
633645
// just tests by whether or not this compiles
634646
fn _empty_impl_all_auto_traits<T>() {
635647
use std::panic::{RefUnwindSafe, UnwindSafe};

0 commit comments

Comments
 (0)