Learning advanced Rust iterators (general help)

greetings everyone

I am trying to improve my Rust skills in using (relatively advanced) iterators in Rust.

I found a lot of questions and answers on this topic.

I am comfortable using more basic/fundamental iterators in idiomatic Rust (things like filter, map, count, min, etc).

I am wondering if there is a (list of) your favourite resources on internet (or books) which cover how to get the most out of Rust iterators from basic to advanced, including traps, best practices, performance tips, etc.

I am looking for things that help dealing with more complex types, Result<>, Options, Enums, Collections, with a lot of filtering, mappings, aggregations, more advanced closures, when to use borrow/by_ref, etc.

There is plenty of material out there but it is spread across hundreds or thousands of posts here and elsewhere and is quite specific.

Perhaps it is well summarized in a handful of places?

Here is one I found, as example: GitHub - rustomax/rust-iterators: Basic Rust iterator usage

its a very good start but it does not cover as much as I hoped, as titles says, Basic Rust, which is does very well.

I was after The Iterator Bible, "everything you wanted to know about iterators but afraid to ask".

It is such an important feature of Rust, particularly in the applications space, that deserves a big fat book on its own, IMO. Furthermore, the parallel iterators (such as provided by rayon crate?) is another big fantastic feature that is worth shouting about for Rust fans.

My apologies if this has been covered already, I failed to find it.

Thank you.

Once you understand most of the basic syntax and use cases, by far the best resource for learning things is to just dive in and write the code.

I mean, sure I could tell you a dozen cool ways you can use iterators, but if I handed that knowledge to you on a silver platter you won't really get a good intuition for how a pattern works or when it is appropriate.

If you want to really learn how these abstractions are created then try to build your own iterators. Here are a couple interesting challenges for you...

  • Create a simple tree and then define depth first and breadth first iterators
  • Write your own implementations for common iterator adaptors like map(), filter(), peek(), and cycle()
  • Try to make your own crappy parallel iterator
  • Try to create an iterator over that tree structure which yields &mut T references instead of &T then tell me why it was so hard to do

Oh, and as you are going try to write your experiments (both successful and failures) up so we have a nice iterator bible we can link to for the next guy :stuck_out_tongue:

4 Likes

write a code like "xxxx..x".iter(). and use the autofill a way and using ctrl+b(clion) to read the resource code.

it provides explanations and examples here.

1 Like

I expect there's not many resources on the topic specifically because there aren't many traps or performance hurdles to overcome - the compiler is powerful enough that, IME, the obvious way to do something with iterators is usually as fast as the imperative equivalent. In some cases, it can be faster because the compiler can reason better and as a result elide bounds checks or vectorize when it wouldn't do so for imperative code.

The only non-obvious trick I can think of with regard to iterators is noticing that Option and Result implement IntoIterator and the result yields Some/Ok values. This means that if you take an iterator and flat_map a fallible function, you get an iterator over exactly the successes.

1 Like

thank you,

re your last paragraph re Option/Result,

" The only non-obvious trick I can think of with regard to iterators is noticing that Option and Result implement IntoIterator and the result yields Some / Ok values. This means that if you take an iterator and flat_map a fallible function, you get an iterator over exactly the successes."

Can you think of an example for each one of those 2 non-obvious cases (one for Option and one for Result) with a fallible function and flat_map (or similar) ?

thank you

I don't have an example for Option, but I recently used that consruct for a server where I had a number of different functions that each tried to parse a type of incoming message and returned a Result indicating whether the parse was successful. I wanted to test an input against all the possible handlers in turn, and could do so very compactly like this:

fn parse(input: &str) -> Option<Message> {
    let parsers: Vec<&dyn Fn(&str) -> Result<Message, Error>> = vec![ /* list of fn references */ ];
    parsers.into_iter().flat_map(|p| p(input)).next()
}

(Although that said, this is maybe excessively dense and I wouldn't necessarily recommend it as good style)

1 Like

thank you for this.
any working example is much better than no example!
cheers!

I wouldn't call it "advanced", but I wrote an article about iterators that you may find interesting:

1 Like

Jon Gjengset's series deserves a shout-out here: Crust of Rust: Iterators - YouTube

2 Likes