Iteration in Rust: How to Stop on Error and Return Result::Err

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why won't the iterator stop?

\n\n

Imagine: you're writing a Rust program that processes a list of numbers — parsing strings, reading files, performing some transformations. You use Iterator::map to go through each element. And then — bam! — one of the elements turns into a Result::Err error. It would be logical to immediately stop the iteration and return this error upward. But map just keeps working as if nothing happened. Why? And most importantly — how to fix it?

\n\n

This question is one of the most popular on Stack Overflow among Rust beginners. And for good reason. Rust's error handling mechanisms are flexible but require a deliberate approach. In this article, we'll break down the problem thoroughly, look at several solutions, and choose the most elegant one. Let's go!

\n\n

The Problem: map doesn't stop

\n\n

Let's start with a classic example. Suppose we have a vector of strings that we want to convert to numbers:

\n\n
fn main() {\n    let inputs = vec!["42", "hello", "100"];\n\n    let result: Vec<Result<i32, std::num::ParseIntError>> =\n        inputs.iter().map(|s| s.parse::<i32>()).collect();\n\n    println!("{:?}", result);\n}
\n\n

This code will compile and run. But what do we get? A vector of Results: [Ok(42), Err(ParseIntError), Ok(100)]. The iteration didn't stop at "hello", but continued further. For many tasks this is fine — you want to collect all errors. But what if you need the first exception? For example, when loading a configuration: if one parameter is invalid, there's no point in checking further.

\n\n

The problem is that map is a lazy transformation. It doesn't know what "stopping" means. It simply says: "Give me an element, I'll apply the function, return the result." All control over pausing and continuing lies with the consumer of the iterator (collect, for, fold, etc.). And collect for Vec just goes all the way to the end.

\n\n

Solution 1: collect with FromIterator (most popular)

\n\n

The most common answer on Stack Overflow is to use collect::<Result<Vec<_>, _>>(). Rust knows that you can collect an iterator of Results into a single Result with a vector inside. If an Err is encountered, collection stops and that error is returned.

\n\n
fn main() -> Result<(), std::num::ParseIntError> {\n    let inputs = vec!["42", "hello", "100"];\n\n    let result: Vec<i32> =\n        inputs.iter()\n              .map(|s| s.parse::<i32>())\n              .collect::<Result<Vec<_>, _>>()?; // <-- magic!\n\n    println!("{:?}", result);\n    Ok(())\n}
\n\n

How does this work? The type Result<Vec<i32>, ParseIntError> implements the FromIterator trait. When collect sees an Err, it immediately returns it without processing the remaining elements. If everything is fine — you get Ok(vector). This is short, idiomatic, and efficient.

\n\n

Downside: you lose information about which element caused the error (unless you wrap it in your own type). But for quick scripts — it's perfect.

\n\n

Solution 2: filter_map for ignoring errors

\n\n

Sometimes you don't want to stop, but simply want to ignore failed elements. For example, counting only valid strings. filter_map is ideal for this:

\n\n
fn main() {\n    let inputs = vec!["42", "hello", "100", "abc", "7"];\n\n    let numbers: Vec<i32> = inputs.iter()\n        .filter_map(|s| s.parse::<i32>().ok()) // .ok() converts Err to None\n        .collect();\n\n    println!("{:?}", numbers); // [42, 100, 7]\n}
\n\n

filter_map takes a

Blogs

Book Recommendations