What is the equivalent of Python's `zip(..., strict=True)` in Rust?

In Python, the zip() function with strict=True ensures that all iterables passed to zip() have the same length. If they don't, it raises a ValueError. This is useful for catching potential errors when one expect all iterables to be of equal length.

What is the equivalent of Python's zip(..., strict=True) in Rust? Or what is conventional to expect all the iterators have the same length in this cast?

Thanks.

When writing code with Rust, the first difference you need to realise with respect to Python is what is considered idiomatic when designing APIs.

Taking your example of the zip helper function, the idiomatic thing to do in Python is to raise an exception.

In Rust this isn't considered idiomatic (The first reason being that Rust doesn't have the concept of exceptions). Instead, APIs in Rust return values that force you to deal with the output immediately, the most common choices being:

  • the Option enum
  • the Result enum
  • some other domain-specific enum

Going back to the zip example, the default behaviour in Rust when zipping two iterators is to join them using the length of the shortest one. If you want to join iterators of different lengths and keep the unmatched elements, you can take a look at itertools zip_longest method, which will return the Both enum variant when the index exists in both iterators, and the Left or Right enum variants when the index only exists in one of them respectively.

3 Likes

Hello, thanks for the hints.

I know about the zip_longest method from itertools. If I want to confirm that 'Oh no! I have zipped two iterators of different lengths together', is using zip_longest the best and most idiomatic way to do this?

I almost have never tried to zip iterators with different lengths. When I use Python, I rely on strict=True to 'get such a notification'.

itertools also has a zip_eq function/method that will panic if the two iterators don't have the same length.

4 Likes

Ah, didn't know that. Thanks for the information.

What I meant was that working with Rust requires a fundamental change in the way we think about code.

I don't like panicking APIs; if for some reason I wanted to be "notified" about zipping two unequal length iterators, I would write my own helper function that would return a Result instead.

pub fn zip_strict<U, T>(a: T, b: T) -> Result<impl Iterator<Item = (U, U)>, String>
where
    T: IntoIterator<Item = U>,
    T::IntoIter: ExactSizeIterator,
{
    let a_iter = a.into_iter();
    let b_iter = b.into_iter();
    if a_iter.len() != b_iter.len() {
        return Err("Not good".to_owned());
    }

    Ok(a_iter.zip(b_iter))
}
1 Like

I write code using zip_eq only when I expect iterators' length is always same. If it's not same, I've written a bug. So panicking this the most reasonable behavior in this case, and returning a Result doesn't help anyway.

2 Likes

I do understand that use case, but I consider it fundamentally different to the "I want to be notified if I'm zipping iterators of unequal length" use case if the function is expected to handle the error case.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.