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


#1

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

#2

Iterator::inspect()


#3

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.


#4

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.


#5

See https://github.com/rust-lang/rfcs/pull/582


#6

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


#7

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.


#8

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


#9

@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).


#10

I have opened a new RFC for this.


#11

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.


#12

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


#13
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.