Question
How can a Result be converted cleanly into an Option in Rust?
The following code collects only string slices that can be parsed as usize values:
fn example(val: &[&str]) {
let parsed_value: Vec<usize> = val
.iter()
.filter_map(|e| e.parse::<usize>())
.collect();
}
However, parse::<usize>() returns Result<usize, ParseIntError>, while filter_map requires its closure to return Option<T>. What is the idiomatic way to convert the parsing result to an option when invalid values should be skipped?
Short Answer
You will learn the difference between Rust's Result and Option, how Result::ok() converts an error into None, and why this works naturally with iterator methods such as filter_map. You will also learn when silently ignoring errors is appropriate and when errors should be preserved instead.
Concept
Option<T> and Result<T, E> both describe computations that may not produce a normal value, but they communicate different information:
Option<T>means a value may or may not exist:Some(value)orNone.Result<T, E>means an operation can succeed or fail:Ok(value)orErr(error).
Parsing can fail for a specific reason, so str::parse returns a Result:
let parsed: Result<usize, _> = "not-a-number".parse();
Iterator::filter_map is designed for a closure that either produces an item (Some) or skips it (None). When an error is intentionally unimportant, convert the Result with Result::ok():
let value: Option<usize> = "42".parse::<usize>().ok();
ok() performs this conversion:
Ok(value)becomesSome(value).Err(error)becomesNone.
This discards the error details. That is exactly what you want when the rule is “keep valid values and skip invalid ones.”
Mental Model
Think of Result as a delivery receipt.
Ok(package)says the delivery succeeded and includes the package.Err(reason)says it failed and explains why.
An Option is a simpler yes-or-no container:
Some(package)means there is a package.Nonemeans there is no package.
Calling .ok() on a Result is like deciding that the failure explanation is no longer needed. A successful delivery becomes Some(package), and every failed delivery becomes None. filter_map then keeps the Some packages and removes the None entries.
Syntax and Examples
Use Result::ok() when an error should become an absent value.
let success: Result<u32, &str> = Ok(10);
let failure: Result<u32, &str> = Err("invalid input");
assert_eq!(success.ok(), Some(10));
assert_eq!(failure.ok(), None);
For parsing inside filter_map, call .ok() after parse:
fn example(values: &[&str]) -> Vec<usize> {
values
.iter()
.filter_map(|text| text.parse::<usize>().ok())
.collect()
}
let numbers = example(&["12", "oops", "30", "-2"]);
assert_eq!(numbers, vec![12, 30]);
Explanation:
text.parse::<usize>()returnsOk(number)for valid non-negative integers andErr(...)otherwise..ok()converts successful parses toSome(number)and failed parses toNone.filter_mapcollects values fromSomeand skipsNone.
When the method name is not ambiguous, a method reference is also concise:
let numbers: Vec<usize> = ["1", "bad", "3"]
.iter()
.filter_map(|text| text.parse::<usize>().ok())
.collect();
The closure is usually clearest because it shows both parsing and error-discarding behavior together.
Step by Step Execution
Consider this code:
let input = ["8", "hello", "21"];
let output: Vec<usize> = input
.iter()
.filter_map(|text| text.parse::<usize>().ok())
.collect();
Execution proceeds as follows:
input.iter()yields&&stritems referring to"8","hello", and"21". Rust automatically dereferences these where needed byparse.- For
"8",parse::<usize>()returnsOk(8). .ok()changesOk(8)intoSome(8).filter_mapreceivesSome(8), so it keeps8.- For
"hello", parsing returnsErr(ParseIntError { ... }). .ok()changes that error intoNone.filter_mapreceivesNone, so it skips this item.- For
"21", parsing producesSome(21)after.ok(), so21is kept. collect()gathers the kept values intovec![8, 21].
The final value is:
assert_eq!(output, vec![8, 21]);
Real World Use Cases
Converting Result to Option is useful when failure means “this item does not qualify,” rather than “stop the program.”
- Cleaning imported data: Keep valid numeric identifiers from a CSV column and skip malformed rows.
- Optional configuration: Try to parse an optional environment variable; use a default when it is absent or invalid, if that policy is acceptable.
- Extracting values from text: Parse numbers found in user-entered tags, logs, or filenames.
- Best-effort batch processing: Process valid records while intentionally ignoring records known to be disposable.
- Lookup-style APIs: Convert a recoverable error into
Nonewhen callers only need to know whether a usable value exists.
For important input, such as payment amounts, permissions, or required application configuration, do not silently discard parse errors. Return or report the error instead.
Real Codebase Usage
In production Rust code, the choice to call .ok() is a policy decision: it says errors are intentionally ignored at this point.
Filtering optional valid values
let ports: Vec<u16> = lines
.iter()
.filter_map(|line| line.trim().parse::<u16>().ok())
.collect();
This is appropriate if invalid lines are expected and do not need reporting.
Recording invalid input while keeping valid data
If errors matter, use a loop or partition-style approach rather than .ok():
let mut valid = Vec::new();
let mut invalid = Vec::new();
for text in ["10", "wrong", "20"] {
match text.parse::<usize>() {
Ok(value) => valid.push(value),
Err(error) => invalid.push((text, error)),
}
}
Failing the whole operation on the first invalid value
Use collect directly into a Result:
fn parse_all(values: &[&str]) -> Result<Vec<usize>, std::num::ParseIntError> {
values.iter().map(|text| text.parse::<usize>()).collect()
}
Here, collect() returns the first parsing error instead of skipping it.
Using a default deliberately
unwrap_or or unwrap_or_else is suitable when a fallback is truly correct:
let retries = "invalid".parse::<u32>().unwrap_or(3);
Prefer .ok() with filter_map when you want to skip invalid items, not replace them.
Common Mistakes
Returning Result directly from filter_map
filter_map requires Option, so this does not compile:
let values: Vec<usize> = ["1", "bad"]
.iter()
.filter_map(|text| text.parse::<usize>())
.collect();
Fix it by converting the result:
.filter_map(|text| text.parse::<usize>().ok())
Using unwrap() for untrusted input
This compiles, but panics when any string is invalid:
let values: Vec<usize> = ["1", "bad"]
.iter()
.map(|text| text.parse::<usize>().unwrap())
.collect();
Use .ok() to skip invalid values, or return a Result to report the failure.
Losing useful error information by habit
This is concise:
let value = user_input.parse::<u16>().ok();
But it makes every failure look the same: None. Do not use it when the caller needs an error message, needs to distinguish failure types, or must reject invalid input.
Expecting filter_map to keep failures
filter_map only retains Some values. Once .ok() converts an error to None, the error is gone. If you need both successful values and errors, handle each Result with match.
Parsing without trimming input
Input from files and forms often contains whitespace:
let value = " 42 ".parse::<usize>(); // Err
Trim it first when whitespace should be accepted:
let value = " 42 ".trim().parse::<usize>(); // Ok(42)
Comparisons
| Approach | Input behavior | Output / effect | Best for |
|---|---|---|---|
result.ok() | Converts Ok and Err | Some(value) or None; error is discarded | Optional or disposable failures |
result.err() | Converts Ok and Err | Some(error) or None; value is discarded | Collecting or inspecting failures |
| `filter_map( | x | x.parse().ok())` | Keeps successful parses |
| `map( | x | x.parse())thencollect::<Result<Vec<_>, _>>()` | Stops on an error |
match result | Handles both cases explicitly | Any behavior you define | Logging, recovery, or different error policies |
unwrap_or(default) | Replaces failures | Always produces a value | A documented, safe fallback exists |
Option is not simply a shorter Result. Use Option when the reason for absence does not matter. Use Result when the reason for failure may matter to a caller, a log, or a recovery path.
Cheat Sheet
// Result<T, E> -> Option<T>
let option = result.ok();
// Ok(value) -> Some(value)
// Err(error) -> None
// Skip values that fail to parse
let values: Vec<u32> = texts
.iter()
.filter_map(|text| text.parse::<u32>().ok())
.collect();
// Keep errors instead
let errors: Vec<_> = texts
.iter()
.filter_map(|text| text.parse::<u32>().err())
.collect();
// Require every parse to succeed
let values: Result<Vec<u32>, _> = texts
.iter()
.map(|text| text.parse::<u32>())
.collect();
Rules to remember:
- Use
.ok()only when discarding error details is intentional. filter_mapexpectsOption, notResult.- Use
collect::<Result<Vec<_>, _>>()when any failure should be returned. - Use
matchwhen successful values and errors both need handling.
FAQ
How do I convert a Result<T, E> to an Option<T> in Rust?
Call .ok(). It converts Ok(value) to Some(value) and Err(error) to None.
Why does filter_map require Option instead of Result?
filter_map has a simple contract: Some means emit an item, and None means skip it. It does not decide how errors should be handled, so convert or handle the Result yourself.
What is the idiomatic way to skip parse errors in Rust?
Use .filter_map(|text| text.parse::<T>().ok()) when skipping invalid values is the intended behavior.
Does .ok() log or handle the error?
No. It discards the error completely. Log it or use match before converting if it is useful.
How can I return an error if one string fails to parse?
Map each string to a Result and collect into Result<Vec<T>, E>:
let parsed: Result<Vec<usize>, _> = values.iter().map(|s| s.parse()).collect();
Can I use .ok() with errors other than parsing errors?
Yes. ok() is defined for every Result<T, E>, regardless of the value or error type.
Should I use unwrap() instead of .ok()?
Usually not for external or uncertain input. unwrap() panics on Err; .ok() skips the failed value by converting it to None.
Mini Project
Description
Build a small parser for comma-separated score data. Real imported data may contain blanks, words, negative values, or numbers outside the allowed range. The project demonstrates using parse, Result::ok(), and filter_map to retain only usable values.
Goal
Create a function that extracts valid scores from a comma-separated string and returns them as Vec<u8>.
Requirements
Parse comma-separated fields from an input string.
Trim whitespace around every field.
Keep only values that parse as u8.
Keep only scores from 0 through 100.
Ignore malformed and out-of-range fields.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.