How to dry run an iterator and how to directly print the iterator

I mean I can do for _ in some_iter {}. But, the iterator can be very long and the syntax can look ugly. Is there any method in iterator to dry run an iterator.
Also, Is there any way I can print the Iteration result using println! without resorting to using .collect::<Vec<_>>()?

One way to run through an iterator while ignoring results is some_iter.for_each(drop).

Some people have also used some_iter.count() for this purpose. But while it's nice and short, it's not terribly clear to the reader. (I saw this used more in the early days, before for_each was added.)

See the iter.format(sep) method from the itertools crate.

2 Likes

Thanks for the help

I'd argue that the for loop mentioned in the OP is more idiomatic than just mapping over drop().

That said, I'd also argue that idioms are more of a guide than a hard set of rules written in stone.

1 Like

I consider .for_each(drop) more idiomatic as it's recommended by std when you ignore the result of .collect().

1 Like

There's no collect call to ignore when using a for loop. Insofar stdlib mentions that at all, it's to mention that most (all?) iterators are lazy, and .collect() os what puts it all in motion.

So by using the for loop you've avoided creating the issue in the first place.

As a side note: what's idiomatic and what's not is a matter of consensus, not a matter of individual opinion.

1 Like

I'm not sure you understood what I meant. I was referring to the must_use of collect, which says to use .for_each(drop). There also seems to be consensus around this if Github reactions are anything to go by.

1 Like

If exhaustion of the iterator is fundamentally what you want, then sure. But that begets the question: why do you want that in the first place? The title of this thread says dry run and print.

The latter part is easily dispatched with, slap a #[derive(Debug)] on the type implementing Iterator and you can print a representation of the iterator.

But what's the use case for a dry run? I mean, doing pretty much anything non-lazily with an Iterator exhausts (at least some elements within) it, so a dry run will prevent actual usage. A workaround for that is to clone the iterator (assuming the iterator is cloneable), that way you can just do the original thing with the first instance and pretty much anything else with the second instance.

BTW, the core reason there's a #[must_use] on the .collect() method is precisely because Iterators are lazy i.e. they don't do anything on their own.

For example, in the piece of code below, the map call is registered but not executed:

let mapped = (0..=10).map(|n| n*n);
1 Like

Sometimes iterators have side effects and it can be also useful for tests - but either way a dry run is what Taran asked for in the original question.

I don't think so, the original PR described the motivation as "using it [collect] for side effects is particularly discouraged". collecting and ignoring the result is functionally identical to .for_each(drop) (unless your FromIterator has side effects), but .for_each(drop) makes it more clear.

I'm arguing that because the standard library recommends to use .for_each(drop) in that warning, it makes it more idiomatic. Also, IMO some_iter.for_each(drop) reads better than for _ in some_iter {} ("for each element in some_iter, drop it" versus "for nothing in some_iter, do nothing"). That being said it's such a minute detail it's probably not worth trying to decide a "correct" solution :).

1 Like

The warning suggests .for_each(drop) for two reasons, IIRC:

  • it's easier for a human to change .collect::<Vec<_>>() to .for_each(drop) than to reformat the expression into a for loop
  • with the most trivial possible body, one might as well use internal iteration in case it matters. (With a complicated body it rarely does, but the smaller the body and the more complex the iterator the more likely it might.)
1 Like