---
title: "19.5 Consuming iterators"
description: "The methods that drive an iterator to a result: collect and the turbofish, sum, count, fold, find, and the any/all short-circuiting checks."
url: "https://learnrust.net/chapter-19/consuming-iterators/"
last_updated: "2026-06-13"
---

# 19.5 Consuming iterators

Adapters build a pipeline but never run it (lesson [19.4](https://learnrust.net/chapter-19/iterator-adapters/)). A **consumer** is the method at the end that actually drives the iterator, calling `next` over and over until it returns `None`, and folds the stream of values into a single result. Consumers are where laziness ends and work happens. We've leaned on `collect` already; this lesson gives it and its siblings their proper treatment.

## collect: gather into a collection

`collect` runs the iterator and gathers every item into a collection. The catch, and the reason the turbofish keeps appearing, is that `collect` can build *many* different collections, a `Vec`, a `String`, a `HashMap`, so you must tell it which one you want. There are two ways, and you've already used the syntax for both in chapter 5.

The first is to annotate the binding's type, and let `collect` follow it:

```rust
fn main() {
    let v = vec![1, 2, 3];
    let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
    println!("{doubled:?}");
}
```

```
[2, 4, 6]
```

The `: Vec<i32>` on the left tells `collect` its target. The second way is the **turbofish** (lesson [5.6](https://learnrust.net/chapter-5/parsing-strings-into-numbers/)), naming the type right on `collect` itself:

```rust
let doubled = v.iter().map(|x| x * 2).collect::<Vec<i32>>();
```

This is the exact same situation as `parse::<i32>()`: a generic function whose return type the compiler can't infer from the arguments, so you name it, either through an annotation or through the `::<>` fish. They're interchangeable; pick whichever reads better. When the element type is obvious from the pipeline, `Vec<_>` lets the compiler fill in the blank: `.collect::<Vec<_>>()`.

`collect` into a `String` joins an iterator of `char`s or `&str`s into one string, and `collect` into a `HashMap` (chapter 18) builds a map from an iterator of `(key, value)` tuples:

```rust
use std::collections::HashMap;

fn main() {
    let shout: String = "hello".chars().map(|c| c.to_ascii_uppercase()).collect();
    println!("{shout}");

    let scores: HashMap<&str, i32> = vec![("ada", 9), ("alan", 7)].into_iter().collect();
    println!("{:?}", scores.get("ada"));
}
```

```
HELLO
Some(9)
```

> **collect is the inverse of iteration**
>
> Iteration takes a collection apart into a stream of items; `collect` puts a stream of items back together into a collection. Which collection is the one question `collect` can't answer on its own, which is why it's the method that most often needs a turbofish or a type annotation. Any time you see `collect` with no obvious target type, that's the missing-type question of lesson [5.6](https://learnrust.net/chapter-5/parsing-strings-into-numbers/), and the same two answers apply.

## sum and count

For the common reductions there are named consumers. `sum` adds up the items; `count` reports how many there are. `sum` needs to know the result type (an annotation does it):

```rust
fn main() {
    let nums = vec![1, 2, 3, 4, 5];
    let total: i32 = nums.iter().sum();
    let how_many = nums.iter().filter(|x| *x % 2 == 0).count();
    println!("total {total}, evens {how_many}");
}
```

```
total 15, evens 2
```

`count` walks the whole iterator just to tally it, so use it when you genuinely need the count of a filtered stream; if you only want the length of a plain vector, `vec.len()` is instant and `vec.iter().count()` is wasteful.

## fold: the general reduction

`sum` is a special case of a more general tool. `fold` carries an **accumulator** through the iteration: you give it a starting value and a closure that combines the accumulator with each item to produce the next accumulator. The final accumulator is the result:

```rust
fn main() {
    let nums = vec![1, 2, 3, 4];
    let product = nums.iter().fold(1, |acc, x| acc * x);
    println!("{product}");
}
```

```
24
```

Read it as: start the accumulator at `1`; for each `x`, replace the accumulator with `acc * x`. So `1*1*2*3*4 = 24`. `sum` is `fold(0, |acc, x| acc + x)` with a nicer name. `fold` is the escape hatch for reductions that don't have a built-in: building a string, finding a running maximum, counting with conditions. When you can't find a named consumer for the summary you want, `fold` can always express it.

## find, any, all: short-circuiting searches

The last group answers questions about the stream, and they're special because they *stop early*. They don't have to walk the whole iterator if the answer is settled sooner.

`find` returns the first item matching a predicate, as an `Option`, and stops at the first match:

```rust
fn main() {
    let nums = vec![1, 3, 5, 8, 9];
    let first_even = nums.iter().find(|x| *x % 2 == 0);
    println!("{first_even:?}");
}
```

```
Some(8)
```

`any` returns `true` if *any* item matches (stopping at the first that does); `all` returns `true` if *every* item matches (stopping at the first that doesn't):

```rust
let nums = vec![2, 4, 6, 7];
println!("{}", nums.iter().any(|x| *x % 2 == 1));   // true  (7 is odd)
println!("{}", nums.iter().all(|x| *x % 2 == 0));   // false (7 isn't even)
```

The short-circuiting is the reason to prefer `any` over, say, `filter(...).count() > 0`: `any` can return the instant it finds one match, even on a million-element (or infinite) iterator, while the count version would dutifully walk everything. Choosing the consumer that asks the precise question lets the iterator do the least possible work.

> **Best practice**
>
> Reach for the most specific consumer that answers your question. "Is there a match?" is `any`, not `filter().count() > 0`. "What's the first match?" is `find`, not `filter().next()`. "How many?" is `count`. The specific consumers are clearer to read *and* let the iterator short-circuit, doing only as much work as the answer requires.

## A note on owned versus borrowed results

One thing that surfaces with consumers: whether you used `iter()` or `into_iter()` (lesson [19.3](https://learnrust.net/chapter-19/the-iterator-trait/)) determines whether you collect references or owned values. `v.iter().collect::<Vec<&i32>>()` gathers references (and `v` survives); `v.into_iter().collect::<Vec<i32>>()` gathers owned values (and consumes `v`). For `Copy` types the adapter `.copied()` (or `.cloned()` for non-`Copy`) converts an iterator of `&T` into one of `T` mid-pipeline, which is why you saw `.copied()` in the `zip` example last lesson, it let the result hold plain `i32`s instead of `&i32`s. The ownership question never goes away; it just rides along through the pipeline.

## Quiz time

**Question #1**

Why does `collect` so often need a turbofish or a type annotation, when `sum` and `count` usually don't?

<details class="solution">
<summary>Show solution</summary>

`collect` can build many different target collections (`Vec`, `String`, `HashMap`, and more) from the same iterator, so the compiler can't infer which one you want; you specify it with a binding annotation (`let v: Vec<_> = ...`) or the turbofish (`.collect::<Vec<_>>()`), exactly the missing-type situation from lesson 5.6. `count` always returns a `usize` and `sum`'s type is usually pinned by an annotation on a single number, so there's less (or nothing) left to infer.

</details>

**Question #2**

Rewrite `vec![1, 2, 3, 4].iter().sum::<i32>()` using `fold`, and explain the two arguments to `fold`.

<details class="solution">
<summary>Show solution</summary>

```rust
vec![1, 2, 3, 4].iter().fold(0, |acc, x| acc + x)   // 10
```

`fold`'s first argument is the starting accumulator (`0`). Its second is a closure taking the current accumulator and the next item, returning the new accumulator (`acc + x`). The final accumulator after the whole iterator is the result. `sum` is just this with a built-in name and a starting value of zero.

</details>

**Question #3**

You want to know whether a list contains any negative number. Why is `list.iter().any(|x| *x < 0)` better than `list.iter().filter(|x| *x < 0).count() > 0`?

<details class="solution">
<summary>Show solution</summary>

`any` short-circuits: it stops and returns `true` the instant it finds the first negative, so on a long (or infinite) list it can finish immediately. The `filter().count() > 0` version walks the *entire* list counting matches before comparing, doing far more work for the same yes/no answer. `any` is also clearer about the question being asked.

</details>

We promised back in chapter 7 that the `for` loop was iterators in disguise. The next lesson ([19.6](https://learnrust.net/chapter-19/for-loops-demystified/)) finally pays that off: exactly how `for` desugars into `next` calls through the `IntoIterator` trait.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
