I found a hackish, unidiomatic way to iterate with indices in online judge just for fun: `(0..).zip(xs)`

It's

  • much shorter
  • easier to type (, and it eliminates the typo of enumerate)
  • convenient if I want to start from an index other than 0
    (Python's enumerate(iterable, start=0) has optional arg to do this; but I didn't found an equivalence in Rust)

One disadvantage I can think is zip maybe not as semantically clear as enumerate;
but I personally feel good.


  • std::ops::{Range, RangeFrom, RangeInclusive} impl'd Trait std::iter::Iterator
    That's why we can directly call any method of Iterator
    without the need to convert it into an Iterator first by something like (2..8).iter()

  • The module std::iter has a function zip
    So the snippet can be alternatively written as std::iter::zip(0.., xs)
    but it's longer and only stable since version 1.59.0

Some extra info
3 Likes

One caveat is that 0.. needs type inference to give you usize, and if inference fails you'll get the fallback i32. You can force 0usize.. if needed.

5 Likes

Another caveat is that .enumerate() preserves ExactSizeIterator, but zip doesn't:

let v = vec![1, 2, 3];
dbg!(v.iter().enumerate().len()); // WORKS
dbg!((0..).zip(&v).len()); // ERROR: RangeFrom is not ExactSizeIterator

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=6d697ddc094786f47f361b4c11405c11

(Though once #[marker] traits are stable zip will be able to.)

8 Likes

Well that's the fault of RangeFrom, but zip does preserve exactness with 0..usize::MAX.

6 Likes

Yet another caveat is that (0..A_BIG_NUMBER).zip(xs).rev() takes a long time to run:

const LIM: usize = 1 << 33;
let a = [11, 12, 13, 14];
for ix in (0..LIM).zip(a).rev() {
    dbg!(ix);
}

(when LIM == 1 << 34, even if built as Release it still times out in the Playground)

My issue with that is "intent communication".

If I stumbled on that piece of code at work, I'd definitely wonder why my coworker wrote it like that, maybe they had a deep reason while figuring out their code that become irrelevant by the time they finished their pull request and forgot about that loop. I'd definitely message them to ask about why this doesn't use the standard idiom, did they have a reason for it.

10 Likes

A related problem I would note is that the shorter way is more vulnerable to typo-induced bugs: if one misspells "enumerate", rustc should be able to catch the mistake, but if one accidentally types 9 rather than 0, it can't know one meant 0.

6 Likes

Thank you all for coming up so many corner cases of bad consequences if use (0..).zip(xs) carelessly!

The driving cause of posting this snippet was to share my pleasure of discovering the trick, not to convince and promote something we should use in any serious work.
It looks like practical usage is preferred, so I changed the title to focus it.

The title before was:
"I found (0..).zip(xs) sweeter than xs.into_iter().enumerate()"

Ah, I apologize for my part in spoiling your fun. I hope you enjoy Rust anyway. :bowing_man:[1]


  1. that's U+1F647 PERSON BOWING DEEPLY, even though Discourse calls it bowing_man for some reason ↩ī¸Ž

1 Like

You are so kind!
You don't need to say sorry, what you had said is correct and I don't think you did anything wrong. And I joined the picking too ; )
It's me that didn't convey my intent clear. And perhaps didn't post in best place? (Maybe better in somewhere like "Rust worse practice kidding" or "OJ in Rust")

Don't number anything starting with 1!

Why numbering should start at zero

Americans skip 0 when numbering building floors, but Europeans don't.

1 Like

The typo part should be handled by your IDE autocompletion (you use an IDE, right?). The easier to type part is debatable: it.into_iter().enumerate() thing is an idiom that you can write automatically, and there is no way to write in wrong, while (0..).zip(it) has more way to be mistyped (what's the lower bound? is it (0..).zip(it) or it.zip(0..)? is there an upper bound? what if you need to reverse your iterator or use larger steps?).

However, if you want a more pythony style of enumerated iteration, you can have it. Add the itertools crate and import use itertools::enumerate;. Then you can write enumerate(vec) instead of vec.into_iter().enumerate(). Personally I find it prettier, but not pretty enough to warrant using a less idiomatic function.

1 Like

Certainly we don't all use IDEs / rust-analyzer, and I hope Rustaceans, at least hobbyists,[1] won't need to — but we do all use rustc,[2] which should suffice to render a typo in "enumerate" harmless.


  1. I'm sure R-A is useful for professionals working on large codebases ↩ī¸Ž

  2. for now, at least — and I would expect a practical alternative compiler to catch this too ↩ī¸Ž