The key is to detect that one iterator is shorter then the other, you could do it before before in your case vector implement ExactSizeIterator
but a general solution would be to have a custom .zip()
.
itertools already offer a general solution, .zip_longest()
:
use itertools::EitherOrBoth::{Both, Left, Right};
use itertools::Itertools;
fn main() {
let num1 = vec![1, 2];
let num2 = vec![3];
for i in num1
.iter()
.rev()
.zip_longest(num2.iter().rev())
.map(|x| match x {
Both(a, b) => (a, b),
Left(a) => (a, &0),
Right(b) => (&0, b),
})
{
println!("{:?}", i);
}
}
This require you write the closure everytime, if you need this feature a lot maybe implement a custom trait on iterator with .zip_default()
where A
and B
implement Default
:
use std::default::Default;
use std::iter::Fuse;
pub trait MyIterTools: Iterator {
fn zip_default<J>(self, other: J) -> ZipDefault<Self, J::IntoIter>
where
J: IntoIterator,
Self: Sized,
{
ZipDefault::new(self, other.into_iter())
}
}
#[derive(Clone, Debug)]
pub struct ZipDefault<I, J> {
i: Fuse<I>,
j: Fuse<J>,
}
impl<I, J> ZipDefault<I, J>
where
I: Iterator,
J: Iterator,
{
fn new(i: I, j: J) -> Self {
Self {
i: i.fuse(),
j: j.fuse(),
}
}
}
impl<T, U, A, B> Iterator for ZipDefault<T, U>
where
T: Iterator<Item = A>,
U: Iterator<Item = B>,
A: Default,
B: Default,
{
type Item = (A, B);
fn next(&mut self) -> Option<Self::Item> {
match (self.i.next(), self.j.next()) {
(Some(a), Some(b)) => Some((a, b)),
(Some(a), None) => Some((a, B::default())),
(None, Some(b)) => Some((A::default(), b)),
(None, None) => None,
}
}
}
impl<T: ?Sized> MyIterTools for T where T: Iterator {}
fn main() {
let num1 = vec![1, 2];
let num2 = vec![3];
for i in num1
.iter()
.copied()
.rev()
.zip_default(num2.iter().copied().rev())
{
println!("{:?}", i);
}
}
Using itertools we can delegate some logic:
use std::default::Default;
use itertools::Itertools;
use itertools::ZipLongest;
use itertools::EitherOrBoth::{Both, Left, Right};
pub trait MyIterTools: Iterator {
fn zip_default<J>(self, j: J) -> ZipDefault<Self, J::IntoIter>
where
Self: Sized,
J: IntoIterator,
{
ZipDefault::new(self, j.into_iter())
}
}
#[derive(Clone, Debug)]
pub struct ZipDefault<I, J> {
inner: ZipLongest<I, J>,
}
impl<I, J> ZipDefault<I, J>
where
I: Iterator,
J: Iterator,
{
fn new(i: I, j: J) -> Self {
Self {
inner: i.zip_longest(j),
}
}
}
impl<T, U, A, B> Iterator for ZipDefault<T, U>
where
T: Iterator<Item = A>,
U: Iterator<Item = B>,
A: Default,
B: Default,
{
type Item = (A, B);
fn next(&mut self) -> Option<Self::Item> {
match self.inner.next()? {
Both(a, b) => Some((a, b)),
Left(a) => Some((a, B::default())),
Right(b) => Some((A::default(), b)),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<T: ?Sized> MyIterTools for T where T: Iterator {}
fn main() {
let num1 = vec![1, 2];
let num2 = vec![3];
for i in num1
.iter()
.copied()
.rev()
.zip_default(num2.iter().copied().rev())
{
println!("{:?}", i);
}
}