DEV Community

loading...
Cover image for Rust standard iterators

Rust standard iterators

dandyvica profile image Alain Viguier ・5 min read

Today, I'll tackle Rust standard iterators, which can be browsed here: Rust Iterators

In you ever read this page, you've probably stumbled upon a list of methods along with some examples, but even after reading this full page, you may have wondered: how to use all these methods ?

Because the examples are based, for the most part, on a vector of integers (which by the way implement the Copy trait), it's pretty easy to use with such an iterable. But once you have a more complicated iterator like a vector of structs, this might be more tedious to use.

That's why I wrote this article.

As the base for my examples, I've use the Mendeleiev periodic list of elements available as a CSV file here: https://gist.github.com/GoodmanSciences/c2dd862cd38f21b0ad36b8f96b4bf1ee (beware to fix lines which contain spaces betweens fields, as Rust might crash when loading the CSV).

I've create a simple function to load the data into a struct, whose members map (snake case though) the CSV column names:

pub fn load_as_vector() -> Result<Vec<Element>, Box<dyn Error>> {
    // load CSV
    let reader = std::fs::File::open("elements.csv").unwrap();
    let mut rdr = csv::Reader::from_reader(reader);

    // create a vector of structs
    let mut v: Vec<Element> = Vec::new();

    for result in rdr.deserialize() {
        let record: Element = result?;
        v.push(record);
    }

    Ok(v)
}
Enter fullscreen mode Exit fullscreen mode

The vector is loaded using this dedicated method (you can find the whole code here: https://github.com/dandyvica/articles/tree/master/combinators)

let v = element::load_as_vector()?;
Enter fullscreen mode Exit fullscreen mode

I didn't respect the alphabetic order of all the methods, I rather adopted an incremental approach, with the simplest ones in the beginning of the article. I tried to express what the method is representing for the vector data, instead of writing its definition which you can get anyway. I also unwrap() the results when possible, because a lot of iterators methods usually return an Option type.

I also didn't cover all the methods, by lack of time. If you get ideas on how to add other examples for this missing methods, feel free to reach out ! In addition, the following examples are probably not optimized and some better combination of iterators should exist.

Beware I'm by no means a chemical engineer, I used this material just to illustrate my article.

Iterator methods

  • count(): there're 118 elements in the Mendeleiev table
let n = v.iter().count();
assert_eq!(n, 118);
Enter fullscreen mode Exit fullscreen mode
  • last(): Oganesson is the last element
let last_element = v.iter().last().unwrap();
assert_eq!(last_element.element, "Oganesson");
Enter fullscreen mode Exit fullscreen mode
  • nth(): Uranium is the 92th element (vector is indexing from 0), but there's not a 119th element
let uranium = v.iter().nth(91).unwrap();
assert_eq!(uranium.element, "Uranium");
assert!(v.iter().nth(118).is_none());
Enter fullscreen mode Exit fullscreen mode
  • map():: Carbon is the 6th element
let mut mapped = v
    .iter()
    .map(|x| (x.atomic_number, x.element.as_ref(), x.symbol.as_ref()));
assert_eq!(mapped.nth(5).unwrap(), (6u8, "Carbon", "C"));
Enter fullscreen mode Exit fullscreen mode
  • collect(): create a vector of elements' names
let names: Vec<_> = v.iter().map(|x| &x.element).collect();
assert_eq!(names[0..2], ["Hydrogen", "Helium"]);
Enter fullscreen mode Exit fullscreen mode
  • take(): the 2 first elements are Hydrogen and Helium
let first_2: Vec<_> = v.iter().take(2).map(|x| x.element.clone()).collect();
assert_eq!(first_2, ["Hydrogen", "Helium"]);
Enter fullscreen mode Exit fullscreen mode
  • take_while(): there're 10 elements with less than 10 neutrons
let less_than_10e = v.iter().take_while(|x| x.number_of_neutrons <= 10);
assert_eq!(less_than_10e.count(), 10);
Enter fullscreen mode Exit fullscreen mode
  • any(): there's at least one element with more than 50 electrons
assert!(v.iter().any(|x| x.number_of_neutrons > 50));
Enter fullscreen mode Exit fullscreen mode
  • all(): all elements have their symbol composed by 1 or 2 letters (e.g.: C or Na)
assert!(v.iter().all(|x| x.symbol.len() == 1 || x.symbol.len() == 2));
Enter fullscreen mode Exit fullscreen mode
  • cycle(): when cycling through elements, Lithium is the 120th element
assert_eq!(v.iter().cycle().nth(120).unwrap().element, "Lithium");
Enter fullscreen mode Exit fullscreen mode
  • find(): Helium is the first element whose name ends with ium
let helium = v.iter().find(|x| x.element.ends_with("ium")).unwrap();
assert_eq!(helium.element, "Helium");
Enter fullscreen mode Exit fullscreen mode
  • filter() and for_each(): there're 11 gases
let gases: Vec<_> = v.iter().filter(|x| x.phase == "gas").collect();
assert_eq!(gases.iter().count(), 11);
v.iter()
    .filter(|x| x.phase == "gas")
    .for_each(|x| println!("{:?}", x.element));
// gives:
// "Hydrogen"
// "Helium"
// "Nitrogen"
// "Oxygen"
// "Fluorine"
// "Neon"
// "Chlorine"
// "Argon"
// "Krypton"
// "Xenon"
// "Radon"
Enter fullscreen mode Exit fullscreen mode
  • filter_map(): there're 37 radioactive elements
let radioactives: Vec<_> = v
    .iter()
    .filter_map(|x| x.radioactive.as_ref())
    .filter(|x| **x == YN::yes)
    .collect();
assert_eq!(radioactives.iter().count(), 37);
Enter fullscreen mode Exit fullscreen mode
  • enumerate(): the last element index is 117
let (i, _) = v.iter().enumerate().last().unwrap();
assert_eq!(i, 117);
Enter fullscreen mode Exit fullscreen mode
  • skip_while(): the first non-gas is Lithium
let first_non_gas = v.iter().skip_while(|x| x.phase == "gas" ).next().unwrap();
assert_eq!(first_non_gas.element, "Lithium");
Enter fullscreen mode Exit fullscreen mode
  • zip(): Uranium mass number is 238
let neutrons = v.iter().map(|x| x.number_of_neutrons);
let protons = v.iter().map(|x| x.number_of_protons);
let mass_numbers: Vec<_> = neutrons.zip(protons).map(|(x, y)| x + y).collect();
assert_eq!(mass_numbers[91], 238);
Enter fullscreen mode Exit fullscreen mode
  • chain(): when listing gases and solids, Lithium is the first element, Radon the last
let all_gases = v.iter().filter(|x| x.phase == "gas");
let all_solids = v.iter().filter(|x| x.phase == "solid");
let gases_and_solids: Vec<_> = all_solids.chain(all_gases).collect();
assert_eq!(gases_and_solids.iter().nth(0).unwrap().element, "Lithium");
assert_eq!(gases_and_solids.iter().last().unwrap().element, "Radon");
Enter fullscreen mode Exit fullscreen mode
  • position(): searches for the Potassium element
let potassium = v.iter().position(|x| x.element == "Potassium").unwrap();
assert_eq!(v[potassium].symbol, "K");
Enter fullscreen mode Exit fullscreen mode
  • rposition(): Radon is the last gas
let last_gas = v.iter().rposition(|x| x.phase == "gas").unwrap();
assert_eq!(v[last_gas].element, "Radon");
Enter fullscreen mode Exit fullscreen mode
  • max_by(): the heaviest non-artificial element is Uranium
use std::cmp::Ordering;
let cmp = |x: &Element, y: &Element| -> Ordering {
    if x.atomic_mass < y.atomic_mass {
        Ordering::Less
    } else if x.atomic_mass > y.atomic_mass {
        Ordering::Greater
    } else {
        Ordering::Equal
    }
};

let heaviest = v
    .iter()
    .filter(|x| x.phase != "artificial")
    .max_by(|x, y| cmp(x, y))
    .unwrap();
assert_eq!(heaviest.symbol, "U");
Enter fullscreen mode Exit fullscreen mode
  • rev(): the last element when reversing the vector is Hydrogen
let hydrogen = v.iter().rev().last().unwrap();
assert_eq!(hydrogen.symbol, "H");
Enter fullscreen mode Exit fullscreen mode
  • max_by_key(): the longuest element's name is Rutherfordium
let longuest = v.iter().max_by_key(|x| x.element.len()).unwrap();
assert_eq!(longuest.element, "Rutherfordium");
Enter fullscreen mode Exit fullscreen mode
  • max(): Carbon was the first element discovered, Tennessine the last
//use std::cmp::Ordering;

impl Ord for Element {
    fn cmp(&self, other: &Self) -> Ordering {
        if self.year < other.year {
            Ordering::Less
        } else if self.year > other.year {
            Ordering::Greater
        } else {
            Ordering::Equal
        }
    }
}

impl PartialOrd for Element {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Element {
    fn eq(&self, other: &Self) -> bool {
        self.year == other.year
    }
}

impl Eq for Element {}

let first_discovered = v.iter().min().unwrap();
assert_eq!(first_discovered.element, "Carbon");
let last_discovered = v.iter().max().unwrap();
assert_eq!(last_discovered.element, "Tennessine");
Enter fullscreen mode Exit fullscreen mode

Hope this helps ! Feel free to comment.

Photo by Bill Oxford on Unsplash

Discussion (0)

pic
Editor guide