Looping in the New World

The one where I learn about Iterators

Old style life saver ring with "save me" printed on it
Save me from old-style coding techniques

I finished the “Reverse” exercise in the last post with my old-style for loop. Turns out, the mentor on Exercism had the perfect level of help in his response to my submission. He pointed out there was a more “Rust-like/idiomatic and concise manner” for looping through the characters. I did some reading.

Rust Roots and Some Suggestions

I’ve never done any functional programming before, so I’ve never used languages like Lisp, Erlang, OCaml, or Haskell (though, I’ve heard those names before). The Rust book points out that inspiration for Rust syntax and technologies was taken from several existing languages – one technology they adopted (from functional programming languages) was Iterators.

I was actually using an iterator in my first solution, with the for in loop, but I didn’t know it. The .chars() method on a String is an Iterator – it implements the std::iter.::Iterator trait. In fact, in the code for i in 1..12, the expression 1..12 is a Range<i32> value and Range<i32> is simply an iterator that spits out the integers from the starting value (inclusive) to the ending value (exclusive). Similarly, the .chars() iterator spits out the UTF-8 characters that make up the string, from the first character through the last one.

BTW, let’s try to remember this inclusive/exclusive fact – I can see it coming back to bite me if I don’t remember that the Range<i32> 1..12 would enter the loop the first time with a value of 1 and the very last time with a value 11!

Anyway, an iterator does the work of knowing how to loop through a collection of items: where to start and when to end. So my solution could have been even more antiquated; I could have had a for 0..input.len() loop.




Also, it turns out I had stumbled onto the .rev() method – which is actually a method of an Iterator type, which technically “reverses an iterator’s direction” – so it will iterate from “right to left”. It’s what I wanted, I just didn’t understand how I got there.

One final step, we want to collect the individual (reversed) string of characters that we’ll be getting out of input.char().rev(). But we don’t need to manually push them into a new string, we can simply .collect them (didn’t I say that already?) A collection of Chars will, of course, .collect into a single String. AND, since that string is exactly what we want to return, our entire function can be the single expression (with no trailing ; so Rust knows to return this value):

input.chars().rev().collect()

Turbofish – One Really Fast Fast!

One final bit of advice from the mentor on Exercism. He explained that it is best to be explicit about the resulting type with generics like .collect() by using the Rust turbofish operator ::<>. This way, it is obvious from the .collect() what type will result (though it’s plain in this simple example). So finally, we get:

pub fn reverse(input: &str) -> String {
     input.chars().rev().collect::<String>()
 }