.map(), .filter(), but no .each()

Hi,

I'm starting with Rust, and with a little background in Ruby I heavily enjoy similarities between the languages. One thing, though, is my lack of understanding why there is no .each() iterator method to call up a closure on an iterator that doesn't have a return value, avoiding the "iterator adapters are lazy and do nothing unless consumed". I did read the respective paragraph from the book, but I don't see why the language shouldn't be able to support a way to declare an iterator such that it fires even without the result being used. The lazy thing about iterators, btw, is a cool thing - could mate quite nicely with automated multi-threaded pipeline processing.

It is so awkward that I can chain iterators, but the final iteration has to be done with a BASIC/Python-style for loop. In Ruby I completely forgot about for because it's so much more intuitive to iterate over the iterator just like in map and filter calls.

I'm just starting with Rust, so I might have not seen yet the discussion about this topic yet.

Thanks for any pointers and reasoning,

  • Mat
1 Like

Iterator::inspect()

The current accepted way of doing this is to either just use a for loop for the final iteration:

let mut iterator = ...;
for x in iterator {
   ...
}

Or to use .map().count():

something.map(|x| { ... }).count()

I think the main reason there isn't a .each/.foreach method in the std library yet is just that it hasn't been decided how such a function would be named/how it would work, or if it is even needed. Because the above two ways of doing this are easy to understand, and currently work, there isn't a direct need for a .each function, and thus it hasn't received a high priority for 1.0. It may or may not be added to the standard library in the future, it just hasn't been decided and stabilized yet.

Edit: @kstep .inspect is also lazy though, like .map, and would still require some kind of consuming function to be run on the iterator.

Yes, I know it, it just .each() caught my eyes and a description “method to call up a closure on an iterator that doesn't have a return value”, and I missed the part about lazy vs eager methods, as I recalled .inspect() immediately.

1 Like

See RFC add foreach to iterators by larroy · Pull Request #582 · rust-lang/rfcs · GitHub

1 Like

I was also asking about this on IRC and was pointed to itertools, in particular the .foreach() method.

1 Like

Sad to see that RFC was closed, I think this would be a really valuable sugar to add to Rust. It is not uncommon for the consumer of an iterator not to be a method of IteratorExt, but a method or function called on each item in the iterator. The case I have run into repeatedly is tx.send() - when the final transformation on the data is to transfer each item across a thread boundary.

Neither binding the iterator and then looping over it nor adding a completely irrelevant consumer is satisfying. Sure, I could depend on another crate, but this absolutely seems like std functionality to me.

I agree. Let's pitch it for the 1.1 release of Rust, to be realistic.

@bluss Note that to get into 1.1, you'll probably need to get the feature accepted into nightlies by the release of 1.0 (when the 1.1 beta is also released).

I have opened a new RFC for this.

1 Like

Use .fold((), |_, x| { … }). It is a more general version of foreach/each which also allows one to optionally build up the return value while iterating.

That is tricky to type (every punctuation mark costs double to type in my world) and noisy to read.

for a in <iterator> { } // 2 shift-punctuations
<iterator>.foreach(|x|{ }); // 6 shift-punctuations
<iterator>.fold((), |_, x| { }); // 9 shift-punctuations

Using this metric it is clear for-loop is superior in being cheapest and requiring only 33% of shift-punctuations to type over the next best alternative.

1 Like