I mentioned it in my original post
One particular use case which I have in mind is implementation of MultiUnzip
from itertools
for Option
.
At the moment multiunzip
method is very likely impossible to implement for Option
as we need Extend
for Option
or specialization feature.
Here is a code I would like to see implemented from the issue I mentioned above.
struct A {
a: u32,
b: u32,
c: u32,
}
let opt = Some(A { a: 1, b: 2, c: 3 });
let non: Option<A> = None;
let (a, b, c): (Option<_>, Option<_>, Option<_>) = opt
.map(|value| (value.a, value.b, value.c))
.multiunzip(); // a = Some(1); b = Some(2); c = Some(3)
let (a, b, c): (Option<_>, Option<_>, Option<_>) = non
.map(|value| (value.a, value.b, value.c))
.multiunzip(); // a = None; b = None; c = None
Here is current implementation of MultiUnzip
which relies on Extend
:
/// An iterator that can be unzipped into multiple collections.
///
/// See [`.multiunzip()`](crate::Itertools::multiunzip) for more information.
pub trait MultiUnzip<FromI>: Iterator {
/// Unzip this iterator into multiple collections.
fn multiunzip(self) -> FromI;
}
macro_rules! impl_unzip_iter {
($($T:ident => $FromT:ident),*) => (
#[allow(non_snake_case)]
impl<IT: Iterator<Item = ($($T,)*)>, $($T, $FromT: Default + Extend<$T>),* > MultiUnzip<($($FromT,)*)> for IT {
fn multiunzip(self) -> ($($FromT,)*) {
// This implementation mirrors the logic of Iterator::unzip resp. Extend for (A, B) as close as possible.
// Unfortunately a lot of the used api there is still unstable (https://github.com/rust-lang/rust/issues/72631).
//
// Iterator::unzip: https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#2825-2865
// Extend for (A, B): https://doc.rust-lang.org/src/core/iter/traits/collect.rs.html#370-411
let mut res = ($($FromT::default(),)*);
let ($($FromT,)*) = &mut res;
// Still unstable #72631
// let (lower_bound, _) = self.size_hint();
// if lower_bound > 0 {
// $($FromT.extend_reserve(lower_bound);)*
// }
self.fold((), |(), ($($T,)*)| {
// Still unstable #72631
// $( $FromT.extend_one($T); )*
$( $FromT.extend(std::iter::once($T)); )*
});
res
}
}
);
}
impl_unzip_iter!();
impl_unzip_iter!(A => FromA);
impl_unzip_iter!(A => FromA, B => FromB);
impl_unzip_iter!(A => FromA, B => FromB, C => FromC);
impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD);
impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE);
impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF);
impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG);
impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH);
impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI);
impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ);
impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK);
impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK, L => FromL);
I experimented with it trying to find a way to implement MultiUnzip
for Option
but as I mentioned we need either specialization or Extend
for Option
and both seems reasonable to have to me, but Extend for Option is easier to get for this particular use case.
This is not the only use case for having Extend for Option, and you may imagine other implementations which try to do different generic conversions between collections/iterables, tuples and may need a special handling for Option or generalization over iterables and Option (as a special case of iterable or single element). For read path it is covered by IntoIterator
trait, but for write it is more complicated.