Iterator

#185 Jun 2026

185. Range<NonZeroU32> — Iterate NonZero Integers Without Re-Wrapping Every Step

NonZeroU32 keeps Option<Id> at 4 bytes — but until Rust 1.96 you couldn’t iterate lo..hi over them. You’d drop back to u32 and re-wrap every step.

NonZeroU32 and friends are great for indices and IDs because Option<NonZeroU32> fits the niche and stays 4 bytes wide. The catch: Range<NonZeroU32> wasn’t an iterator. The moment you wanted to walk a range of IDs, you fell back to plain u32 and unwrapped your way back in:

1
2
3
4
5
6
7
8
9
use std::num::NonZeroU32;

let lo = NonZeroU32::new(1).unwrap();
let hi = NonZeroU32::new(5).unwrap();

// Pre-1.96: lift, iterate plain ints, re-wrap each step.
let ids: Vec<NonZeroU32> = (lo.get()..hi.get())
    .map(|n| NonZeroU32::new(n).unwrap())
    .collect();

Three problems: the Range drops the invariant, every step pays for an unwrap, and a future refactor that changes the bound type silently swaps your iterator out from under you.

Rust 1.96 stabilized Step for NonZero integers (PR #127534). Now the range itself is an iterator that yields NonZeroU32:

1
2
3
4
5
6
7
8
use std::num::NonZeroU32;

let lo = NonZeroU32::new(1).unwrap();
let hi = NonZeroU32::new(5).unwrap();

let ids: Vec<NonZeroU32> = (lo..hi).collect();
assert_eq!(ids.len(), 4);
assert_eq!(ids[0].get(), 1);

It works for the inclusive form too, so you can sweep the whole representable range without overflow gymnastics:

1
2
3
4
5
6
use std::num::NonZeroU8;

let total: u32 = (NonZeroU8::MIN..=NonZeroU8::MAX)
    .map(|n| n.get() as u32)
    .sum();
assert_eq!(total, 32_640); // 1 + 2 + ... + 255

If you keep your IDs in a NonZeroU32 newtype to shrink Option, the iteration story now matches: the range yields the right type the whole way through, no per-step unwrap, no invariant laundering through u32.

#179 Jun 2026

179. Iterator::max_by_key — Find the Best Element Without a Manual Fold

Finding the “best” item in a collection — longest string, heaviest order, latest timestamp — is one of those tasks where a hand-rolled fold keeps showing up. Iterator::max_by_key does the same job in one call, and min_by_key is right there next to it.

The fold you keep writing

You have a slice of strings and want the longest one. The DIY version looks something like this:

1
2
3
4
5
6
7
8
9
let words = ["pear", "raspberry", "fig", "kiwi"];

let longest = words.iter().fold(None, |best, w| match best {
    None => Some(w),
    Some(b) if w.len() > b.len() => Some(w),
    other => other,
});

assert_eq!(longest, Some(&"raspberry"));

That’s a lot of code for a one-liner concept. max_by_key collapses the whole pattern:

1
2
3
let words = ["pear", "raspberry", "fig", "kiwi"];
let longest = words.iter().max_by_key(|w| w.len());
assert_eq!(longest, Some(&"raspberry"));

You hand it a closure that extracts the key — the thing you want to maximise — and it returns the item that produced the largest key.

Works on anything Ord

The key doesn’t have to be a number. It just has to implement Ord, which means strings, tuples, dates, your own types — anything totally ordered:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#[derive(Debug, PartialEq)]
struct Order { id: u32, total_cents: u64 }

let orders = vec![
    Order { id: 1, total_cents: 1299 },
    Order { id: 2, total_cents: 4500 },
    Order { id: 3, total_cents: 800 },
];

let biggest = orders.iter().max_by_key(|o| o.total_cents);
assert_eq!(biggest, Some(&Order { id: 2, total_cents: 4500 }));

Tuples are where this really shines — you get multi-key sorting for free, because (A, B): Ord compares lexicographically:

1
2
3
4
5
let words = ["pear", "fig", "kiwi", "lime"];

// Longest word, then alphabetically last among ties.
let pick = words.iter().max_by_key(|w| (w.len(), *w));
assert_eq!(pick, Some(&"pear"));

Same trick with min_by_key — the entire _by_key family follows the same shape.

The tie-break rule, and why it matters

When two elements produce equal keys, max_by_key returns the last one and min_by_key returns the first. That asymmetry is in the docs and it bites people:

1
2
3
4
5
6
7
let nums = [3, 1, 4, 1, 5, 9, 2, 6, 5];

let max = nums.iter().max_by_key(|&&n| n);
let min = nums.iter().min_by_key(|&&n| n);

assert_eq!(max, Some(&9));
assert_eq!(min, Some(&1)); // the first 1, not the second

If you need a specific tie-break — “longest word, but earliest in the list when tied” — encode it in the key itself with a tuple, instead of relying on iteration order.

Floats need min_by / max_by

f32 and f64 don’t implement Ord (because NaN), so max_by_key won’t compile if your key is a float. Reach for max_by and pass a comparator instead:

1
2
3
4
5
6
let measurements = [1.2_f64, 3.4, 0.5, 2.8];

let biggest = measurements.iter()
    .max_by(|a, b| a.partial_cmp(b).unwrap());

assert_eq!(biggest, Some(&3.4));

Or wrap your floats in something like ordered_float::OrderedFloat and stay with max_by_key.

Takeaway

Any time you find yourself folding to track the “best so far,” check whether max_by_key or min_by_key says it in one line. The closure extracts the score; the iterator returns the winner. For ties you control the rule by shaping the key.

#174 May 2026

174. iter::repeat_with — Build N Fresh Things When vec![] Won't Clone

vec![Mutex::new(0); 10] — won’t compile. Mutex isn’t Clone. iter::repeat_with(|| Mutex::new(0)).take(10).collect() builds ten fresh ones instead.

The vec![x; n] trap

The vec![x; n] macro is the obvious way to build a Vec of n copies. It works by cloning x n times:

1
2
let zeros: Vec<u32> = vec![0; 5];
assert_eq!(zeros, vec![0, 0, 0, 0, 0]);

That’s fine for u32. It falls apart the moment you want something that isn’t Clone, or something where cloning gives you the wrong behavior — a counter, a channel endpoint, an Arc<Mutex<...>> you wanted to be separate locks:

1
2
3
use std::sync::Mutex;
// error[E0277]: the trait `Clone` is not implemented for `Mutex<u32>`
// let locks = vec![Mutex::new(0); 10];

The fix: iter::repeat_with

iter::repeat_with(f) takes a closure and yields f() every time the iterator is polled. Combine with take(n).collect() and you’ve got a builder that calls the closure exactly n times — no Clone required:

1
2
3
4
5
use std::iter;
use std::sync::Mutex;

let locks: Vec<Mutex<u32>> = iter::repeat_with(|| Mutex::new(0)).take(10).collect();
assert_eq!(locks.len(), 10);

Each Mutex is freshly constructed. They’re independent locks, not ten handles to the same one.

Stateful closures work too

Because the closure is FnMut, it can capture and mutate state — handy for counters, RNG-like generators, or anything where each element depends on the previous call:

1
2
3
4
5
use std::iter;

let mut n = 0;
let counts: Vec<u32> = iter::repeat_with(|| { n += 1; n }).take(5).collect();
assert_eq!(counts, vec![1, 2, 3, 4, 5]);

Try that with vec![...; 5] and you’d get five copies of the same number.

The function-pointer form

When the closure is just a constructor call, you can pass the function directly — no || needed:

1
2
3
4
5
use std::iter;

let buffers: Vec<Vec<i32>> = iter::repeat_with(Vec::new).take(3).collect();
assert_eq!(buffers.len(), 3);
assert!(buffers.iter().all(|b| b.is_empty()));

Vec::new here is a zero-argument function pointer, exactly what repeat_with wants.

When to reach for it

Any time you’d write a for loop to push n fresh items into a Vec, or any time vec![x; n] rejects you because the element isn’t Clone. It’s also the idiomatic way to seed parallel structures: repeat_with(|| Arc::new(Mutex::new(...))).take(workers).collect() gives each worker its own lock.

Available since Rust 1.28, lives in core::iter, no allocation overhead beyond the Vec itself.

168. collect::<Result<Vec<_>, _>>() — Bail an Iterator on the First Error

You have a Vec<&str> of numbers to parse and you want to bail the moment one of them is malformed. The hand-rolled loop with ? works, but collect() does the same thing in one line — and most people never realize the trick is in the turbofish.

The pain point is everywhere: parse a row of CSV cells, deserialize a batch of records, convert a list of paths — any time you map a fallible function over an iterator and want the first failure to stop the show.

The naive version builds the Vec by hand and threads ? through a loop:

1
2
3
4
5
6
7
fn parse_all_loop(inputs: &[&str]) -> Result<Vec<i32>, std::num::ParseIntError> {
    let mut nums = Vec::with_capacity(inputs.len());
    for s in inputs {
        nums.push(s.parse::<i32>()?);
    }
    Ok(nums)
}

The same thing in one line, by asking collect to produce a Result<Vec<_>, _> instead of a Vec<Result<_, _>>:

1
2
3
fn parse_all(inputs: &[&str]) -> Result<Vec<i32>, std::num::ParseIntError> {
    inputs.iter().map(|s| s.parse::<i32>()).collect()
}

That works because Result<C, E> (where C: FromIterator<T>) implements FromIterator<Result<T, E>>: feed it an iterator of Results and it yields a single Result. The first Err short-circuits the rest of the iteration; if every item is Ok, you get Ok(collection) back.

1
2
3
4
5
6
7
8
let good = ["1", "2", "3"];
let bad  = ["1", "oops", "3"];

let ok: Result<Vec<i32>, _> = good.iter().map(|s| s.parse()).collect();
assert_eq!(ok.unwrap(), vec![1, 2, 3]);

let err: Result<Vec<i32>, _> = bad.iter().map(|s| s.parse()).collect();
assert!(err.is_err());

The same impl exists for Option, so collect::<Option<Vec<_>>>() bails on the first None:

1
2
3
4
5
let some_all: Option<Vec<u32>> = ["1", "2", "3"].iter().map(|s| s.parse().ok()).collect();
assert_eq!(some_all, Some(vec![1, 2, 3]));

let some_none: Option<Vec<u32>> = ["1", "no", "3"].iter().map(|s| s.parse().ok()).collect();
assert_eq!(some_none, None);

You’re not stuck with Vec either — any FromIterator target works, so Result<HashSet<_>, _> or Result<String, _> from a Chars iterator is just as valid.

Use it whenever you’d otherwise write a fold-the-error loop. The intent reads off the type signature, and the iterator stops cold at the first bad item instead of finishing the parse and then sifting through a Vec<Result<_, _>> afterwards.

#134 May 2026

134. Iterator::find_map — Find and Transform in One Pass

Looking for the first element that matches and needs to come back as something else? Skip the filter_map(...).next() two-step — find_map says it in one call.

The Problem

You have an iterator and want the first item that satisfies a condition plus the value derived from it. The hand-rolled version is a for loop with a mutable binding and a break:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let inputs = ["abc", "12", "def", "34"];

let mut first: Option<i32> = None;
for s in &inputs {
    if let Ok(n) = s.parse::<i32>() {
        first = Some(n);
        break;
    }
}
assert_eq!(first, Some(12));

You can compress it with filter_map(...).next():

1
2
3
4
5
let first: Option<i32> = inputs
    .iter()
    .filter_map(|s| s.parse::<i32>().ok())
    .next();
assert_eq!(first, Some(12));

It’s shorter, but what you actually mean — find the first one — is buried inside “filter everything, then take one.”

The Fix: find_map

Iterator::find_map takes a closure returning Option<U> and returns the first Some(U) it produces — short-circuiting as soon as the closure says yes:

1
2
3
4
let first: Option<i32> = inputs
    .iter()
    .find_map(|s| s.parse::<i32>().ok());
assert_eq!(first, Some(12));

Same short-circuit behavior as find, but the closure does the transformation too — no separate map step on the result.

Where It Earns Its Keep

Looking up the first input that’s in a small table:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fn level(name: &str) -> Option<u8> {
    match name {
        "trace" => Some(0),
        "debug" => Some(1),
        "info"  => Some(2),
        _ => None,
    }
}

let inputs = ["??", "huh", "info", "debug"];
let first = inputs.iter().find_map(|s| level(s));
assert_eq!(first, Some(2));

Pulling the first error out of a batch of Results without losing the message:

1
2
3
4
let results: Vec<Result<i32, &str>> = vec![Ok(1), Ok(2), Err("bad row"), Ok(3)];

let first_err = results.iter().find_map(|r| r.as_ref().err().copied());
assert_eq!(first_err, Some("bad row"));

Anywhere you catch yourself writing .filter_map(...).next() or a manual loop with break, find_map says the same thing with less noise.

#108 Apr 2026

108. Iterator::max_by_key — Find the Biggest Without Sorting the Whole Thing

Need the longest string in a Vec? Don’t sort the whole list to grab the last element — max_by_key walks once, allocates nothing, and returns it directly.

The wasteful way

You want the longest word from a list. The first instinct is often to sort and pick:

1
2
3
4
5
6
7
let mut words = vec!["fig", "apple", "kiwi", "blackberry", "pear"];

// Sort the full vec by length, then grab the last element.
words.sort_by_key(|w| w.len());
let longest = words.last().unwrap();

assert_eq!(*longest, "blackberry");

That’s O(n log n) work — and a side effect, since your Vec is now reordered — just to answer “which one is biggest?”.

Enter max_by_key

max_by_key walks the iterator once, tracks the running maximum, and hands you the element it picked:

1
2
3
4
5
let words = vec!["fig", "apple", "kiwi", "blackberry", "pear"];

let longest = words.iter().max_by_key(|w| w.len()).unwrap();

assert_eq!(*longest, "blackberry");

O(n), no allocation, no mutation. The original Vec is untouched and you get a &&str back without cloning anything.

Same energy: min_by_key

The mirror method is just as useful — find the shortest word in one pass:

1
2
3
4
5
let words = vec!["fig", "apple", "kiwi", "blackberry", "pear"];

let shortest = words.iter().min_by_key(|w| w.len()).unwrap();

assert_eq!(*shortest, "fig");

Picking by computed value

The closure can do real work — compute distance, score, age — anything that returns something Ord:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
struct Player { name: &'static str, score: u32 }

let roster = [
    Player { name: "Ferris", score: 80 },
    Player { name: "Corro",  score: 95 },
    Player { name: "Crusty", score: 60 },
];

let top = roster.iter().max_by_key(|p| p.score).unwrap();

assert_eq!(top.name, "Corro");

No need to sort, no need to implement Ord on the struct — just point at the field that decides.

Tiebreaks: last one wins

When two elements share the same key, max_by_key returns the last one and min_by_key returns the first:

1
2
3
4
5
6
7
let v = vec![(1, 'a'), (3, 'b'), (3, 'c'), (2, 'd')];

let max = v.iter().max_by_key(|(k, _)| *k).unwrap();
let min = v.iter().min_by_key(|(k, _)| *k).unwrap();

assert_eq!(*max, (3, 'c')); // last 3
assert_eq!(*min, (1, 'a')); // first 1

Worth knowing if your data has duplicates — pick the iteration direction so the right element wins.

When to reach for it

If you catch yourself sorting and then grabbing first(), last(), or [0], stop. min_by_key and max_by_key are the targeted tools: one pass, zero allocations, and the result type is exactly the element you wanted.

#106 Apr 2026

106. Iterator::by_ref — Take Part of an Iterator and Keep the Rest

Called iter.take(n).collect() and watched the borrow checker swallow the whole iterator? by_ref lets you consume a chunk and keep iterating from where you stopped.

The trap: adapters consume their iterator

Most iterator adapters take self, not &mut self. The moment you write iter.take(2), you have moved iter into the new Take adapter. The original binding is gone:

1
2
3
4
5
6
let v = vec![1, 2, 3, 4, 5];
let mut iter = v.into_iter();

let _first_two: Vec<i32> = iter.take(2).collect();
// let rest: Vec<i32> = iter.collect();
// ^^ error: use of moved value: `iter`

So even though there are clearly elements left, the variable that points at them is no longer usable.

The fix: iter.by_ref()

Iterator::by_ref returns a &mut Self — and &mut I itself implements Iterator whenever I: Iterator. Adapters chained off by_ref() consume from the underlying iterator without moving it:

1
2
3
4
5
6
7
8
let v = vec![1, 2, 3, 4, 5];
let mut iter = v.into_iter();

let first_two: Vec<i32> = iter.by_ref().take(2).collect();
let rest: Vec<i32> = iter.collect();

assert_eq!(first_two, [1, 2]);
assert_eq!(rest, [3, 4, 5]);

Same iterator, two phases, no clones, no indexing.

A real use: parse a header, then a body

The pattern shines when the front of a stream uses different rules than the rest. Pull lines until you hit a blank one, then hand the same iterator to the body parser:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let input = "Subject: hi\nFrom: a@b\n\nbody line 1\nbody line 2";
let mut lines = input.lines();

let headers: Vec<&str> = lines
    .by_ref()
    .take_while(|l| !l.is_empty())
    .collect();

let body: Vec<&str> = lines.collect();

assert_eq!(headers, ["Subject: hi", "From: a@b"]);
assert_eq!(body, ["body line 1", "body line 2"]);

Without by_ref, take_while would have consumed lines whole and the body would be unreachable.

Gotcha: take_while peeks one past

take_while reads the first non-matching element to know when to stop — and that element is gone from the underlying iterator. With by_ref you’ll see this directly:

1
2
3
4
5
6
7
let mut nums = (1..).into_iter();

let small: Vec<i32> = nums.by_ref().take_while(|&n| n < 3).collect();
let next = nums.next();

assert_eq!(small, [1, 2]);
assert_eq!(next, Some(4)); // 3 was eaten by take_while

If you need to keep the boundary element, reach for Peekable instead.

When to use it

Any time you want to apply an adapter to part of an iterator and continue afterwards: chunked parsing, header/body splits, “take the first N then process the rest”, consuming until a sentinel. by_ref() turns “I moved my iterator and now I can’t use it” into a single extra method call.

94. ControlFlow::is_break and is_continue — Ask the Flow Which Way It Went

Got a ControlFlow back from try_fold or a visitor and just want to know which variant you’re holding? Before 1.95 you either pattern-matched or reached for matches!. Rust 1.95 adds straightforward .is_break() and .is_continue() methods.

The old pain

ControlFlow<B, C> is the enum that powers short-circuiting iterator methods like try_for_each and try_fold. Once you had one, checking which arm it was took more ceremony than you’d expect:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use std::ops::ControlFlow;

fn first_over(v: &[i32], n: i32) -> ControlFlow<i32> {
    v.iter().try_for_each(|&x| {
        if x > n { ControlFlow::Break(x) } else { ControlFlow::Continue(()) }
    })
}

fn main() {
    let flow = first_over(&[1, 2, 3, 10, 4], 5);
    // Want a bool? Reach for matches!
    let found = matches!(flow, ControlFlow::Break(_));
    assert!(found);
}

The name ControlFlow::Break also collides visually with loop break, so code like this reads a bit awkwardly.

The fix: inherent is_break / is_continue

Rust 1.95 stabilises two one-line accessors, mirroring Result::is_ok / is_err and Option::is_some / is_none:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
use std::ops::ControlFlow;

fn first_over(v: &[i32], n: i32) -> ControlFlow<i32> {
    v.iter().try_for_each(|&x| {
        if x > n { ControlFlow::Break(x) } else { ControlFlow::Continue(()) }
    })
}

fn main() {
    let hit = first_over(&[1, 2, 3, 10, 4], 5);
    let miss = first_over(&[1, 2, 3], 5);

    assert!(hit.is_break());
    assert!(!hit.is_continue());

    assert!(miss.is_continue());
    assert!(!miss.is_break());
}

No pattern, no import of a macro, no wildcards.

Handy in counting and filtering

Because the methods take &self, you can pipe results straight through iterator adapters:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
use std::ops::ControlFlow;

fn main() {
    let flows: Vec<ControlFlow<&'static str, i32>> = vec![
        ControlFlow::Continue(1),
        ControlFlow::Break("boom"),
        ControlFlow::Continue(2),
        ControlFlow::Break("fire"),
        ControlFlow::Continue(3),
    ];

    let breaks = flows.iter().filter(|f| f.is_break()).count();
    let continues = flows.iter().filter(|f| f.is_continue()).count();

    assert_eq!(breaks, 2);
    assert_eq!(continues, 3);
}

Previously you’d write |f| matches!(f, ControlFlow::Break(_)) — shorter, but noisier and requires you to name the variant (which means a use or a fully-qualified path).

It’s just a bool — but it’s the right bool

Nothing here is groundbreaking: you could always write a matches!. But when a method exists on Result and Option and Poll and hash iterators and even Peekable, not having one on ControlFlow meant reaching for a different idiom for no good reason. 1.95 closes that small gap.

Stabilised in Rust 1.95 (April 2026).

#027 Jan 2023

27. Option's sum/product

Rust’s option implements Sum and Product traits too!

Use it when you want to get None if there is a None element and sum of values otherwise.

1
2
3
let nums: [Option<u32>;3] = [Some(1), Some(10), None];
let maybe_nums: Option<u32> = nums.into_iter().sum();
assert_eq!(maybe_nums, None);

or sum of the values …

1
2
3
let nums = [Some(1), Some(10), Some(100)];
let maybe_nums: Option<u32> = nums.into_iter().sum();
assert_eq!(maybe_nums, Some(111));

And product.

1
2
3
let nums = [Some(1), Some(10), Some(100)];
let maybe_nums: Option<u32> = nums.into_iter().product();
assert_eq!(maybe_nums, Some(1000));
#026 Jan 2023

26. Collecting into Option

Rust’s option implements FromIterator too!

Use it when you want to get None if there is a None element and values otherwise.

1
2
3
let cars = [Some("Porsche"), Some("Ferrari"), None];
let maybe_cars: Option<Vec<_>> = cars.into_iter().collect();
assert_eq!(maybe_cars, None);

or values …

1
2
3
let cars = [Some("Porsche"), Some("Ferrari"), Some("Skoda")];
let maybe_cars: Option<Vec<_>> = cars.into_iter().collect();
assert_eq!(maybe_cars, Some(vec!["Porsche", "Ferrari", "Skoda"]));
#025 Dec 2022

25. Option's iterator

Rust’s option implements an iterator!

1
2
3
4
let o: Option<u32> = Some(200u32);
let mut iter = o.into_iter();
let v = iter.next();
assert_eq!(v, Some(200u32));

Why is this useful? see example.

1
2
3
4
5
6
let include_this_pls = Some(300u32);
let r: Vec<u32> = (0..2).chain(include_this_pls).chain(2..5).collect();
assert_eq!(r, vec![0,1,300,2, 3,4]);
let but_not_this = None;
let r: Vec<u32> = (0..2).chain(but_not_this).chain(2..5).collect();
assert_eq!(r, vec![0,1,2,3,4]);
#024 Dec 2022

24. ..=X

As of 1.66, it is possible to use ..=X in patterns

1
2
3
4
5
6
let result = 20u32;

match result {
    0..=20 => println!("Included"),
    21.. => println!("Sorry"),
}
#023 Dec 2022

23. Enum's default value

Instead of manually implementing Default trait for an enum, you can derive it and explicitly tell which variant should be the default one.

1
2
3
4
5
6
7
8
9
#[derive(Default)]
enum Car{
    #[default]
    Porsche,
    Ferrari,
    Skoda
}

let car = Car::default();
#022 Nov 2022

22. Enum's Debug

Use Debug trait to print enum values if needed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#[derive(Default,Debug)]
enum Car{
    #[default]
    Porsche,
    Ferrari,
    Skoda
}

let car = Car::default();
println!("{:?}", car);
#021 Oct 2022

21. Zip longest

Sometimes there is a need to zip two iterables of various lengths.

If it is known which one is longer, then use the following approach:

First one must be longer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
let nums1 = [1, 2, 3, 4];
let nums2 = [10, 20];

for (n1, n2) in nums1
    .into_iter()
    .zip(nums2.into_iter().chain(std::iter::repeat(0i32)))
{
    println!("{n1} - {n2}");
}
// Output:
// 1 - 10
// 2 - 20
// 3 - 0
// 4 - 0
#020 Sep 2022

20. let-else statements

As of 1.65, it is possible to use let statement with a refutable pattern.

1
2
3
4
5
6
7
let result: Result<i32, ()> = Ok(20);

let Ok(value) = result else {
  panic!("Heeeelp!!!");
};

assert_eq!(value, 20);
#019 Aug 2022

19. breaking from labeled blocks

As of 1.65, it is possible to label plain block expression and terminate that block early.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let result = 'block: {
    let result = 20i32;
    if result < 10 {
        break 'block 1;
    }
    if result > 10 {
        break 'block 2;
    }
    3
};
assert_eq!(result, 2);
#018 Jul 2022

18. flatten options

Use flatten to iterate over only Some values if you have a collection of Options.

1
2
3
4
5
6
let nums = vec![None, Some(2), None, Some(3), None];

let mut iter = nums.into_iter().flatten();

assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(3));
#015 Apr 2022

15. Scan

Iterator adapter similar to fold (see previous bite) - holds some internal state and produces new iterator.

Note that Option is yielded from the closure.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let nums = [1, 2, 3];

let mut iter = nums.iter().scan(0, |acc, &x| {
    *acc = *acc + x;
    Some((*acc, x))
});

assert_eq!(iter.next(), Some((1, 1)));
assert_eq!(iter.next(), Some((3, 2)));
assert_eq!(iter.next(), Some((6, 3)));
#014 Apr 2022

14. Find index of item

Use position to find position of an element in iterator. Returns None if the element does not exist.

1
2
3
4
5
6
let nums = [1, 2, 3];

let pos = nums.iter().position(|value| *value == 3);
assert_eq!(pos, Some(2));
let pos = nums.iter().position(|value| *value == 10);
assert_eq!(pos, None);
#017 Mar 2022

17. filter_map

Similar to map but if allows to drop an item.

1
2
3
4
5
6
7
8
let text_nums = ["1", "two", "three", "4"];

let nums: Vec<_> = text_nums
    .iter()
    .filter_map(|x| x.parse::<u32>().ok())
    .collect();

assert_eq!(nums, vec![1, 4]);
#013 Mar 2022

13. Fold

Iterator consumer. Allows the accumulated value to be arbitrary type. Note the different types.

1
2
3
let nums: Vec<u8> = vec![1, 2, 3, 4];
let sum = nums.iter().fold(0u32, |acc, x| acc + *x as u32);
assert_eq!(sum, 10);
#012 Mar 2022

12. Enumerate

Use enumerate to convert iterator over (usize,item) pairs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let cars = vec!["Skoda", "Ferrari", "Ford"];

for (idx, car) in cars.iter().enumerate() {
    println!("{idx} - {car}");
}

// Output
// 0 - Skoda
// 1 - Ferrari
// 2 - Ford

Useful when index of item is needed.

#010 Feb 2022

10. cloned vs copied

Rust provides built-in methods to copy or clone elements when using an iterator.

Eg.

1
let nums: Vec<u32> = nums1.iter().chain(&nums2).copied().collect();

What’s the difference? and when is it useful?

You may have done something like this:

1
let nums: Vec<u32> = nums1.iter().chain(&nums2).map(|x| *x).collect();

but instead, use copied.

1
let nums: Vec<u32> = nums1.iter().chain(&nums2).copied().collect();

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
let nums = vec![1,2,3,4];
let nums2 = vec![5,6,7,8];

// doiing it manually by copying
// let all_nums: Vec<u32> = nums.iter().chain(&nums2).map(|x| *x).collect();

// doiing it manually by cloneing
// let all_nums: Vec<u32> = nums.iter().chain(&nums2).map(|x| x.clone()).collect();

// but why not just use provided functionality ??!
let all_nums: Vec<u32> = nums.iter().chain(&nums2).copied().collect();

// or cloned if copy it is not an option
//let all_nums: Vec<u32> = nums.iter().chain(&nums2).cloned().collect();

 assert_eq!(all_nums, vec![1,2,3,4,5,6,7,8]);

Difference between cloned and copied?

Same reasoning aplies as for Copy and Clone traits. Use copied to avoid to accidentally cloing iterator elements. Use copied when possible.

#009 Feb 2022

9. Inspecting iterator

Ever wondered how to print while iterating?

Use inspect.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let cars = vec!["Skoda", "Ferrari", "Ford"];

let car_lenghts: Vec<u32> = cars
    .iter()
    .enumerate()
    .inspect(|(idx, s)| println!("{idx} - {s}"))
    .map(|(_, name)| name.len() as u32)
    .collect();

assert!( car_lenghts == vec![5,7,4
#008 Feb 2022

8. Drain

Use drain to remove specified range from a vector.

1
2
3
4
5
let mut a = vec![1,2,3,4,5];

let _ = a.drain(0..3);

assert!(a == vec![4,5]);

What is the difference to call into_iter which returns T ?

into_iter takes the collection by value and consumes it.

drain borrows mutable reference, and returns a drain iterator of elements. If such iterator is dropped without exhausting it, all elements are dropped.

#007 Feb 2022

7. iter() vs into_iter()

What is the difference between .iter() and .into_iter()?

iter yields &T

into_iter may yield any of T, &T or &mut T based on context.

1
2
3
4
5
6
7
8
9
let cars = vec!["Skoda", "Ferrari", "Ford"];

for car in cars.iter() {
    println!("{car}");
}

for car in cars.into_iter() {
    println!("{car}");
}

this works but …

This does not. This results in compile error because cars are moved due to into_iter call.

1
2
3
4
5
6
7
8
9
let cars = vec!["Skoda", "Ferrari", "Ford"];

for car in cars.into_iter() {
  println!("{car}");
}

for car in cars.iter() {
    println!("{car}");
}