Question

Convert Result to Option in Rust with ok()

rustoption-type

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) or None.
  • Result<T, E> means an operation can succeed or fail: Ok(value) or Err(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) becomes Some(value).
  • Err(error) becomes None.

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.
  • None means 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.

Take Quiz

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>() returns Ok(number) for valid non-negative integers and Err(...) otherwise.
  • .ok() converts successful parses to Some(number) and failed parses to None.
  • filter_map collects values from Some and skips None.

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:

  1. input.iter() yields &&str items referring to "8", "hello", and "21". Rust automatically dereferences these where needed by parse.
  2. For "8", parse::<usize>() returns Ok(8).
  3. .ok() changes Ok(8) into Some(8).
  4. filter_map receives Some(8), so it keeps 8.
  5. For "hello", parsing returns Err(ParseIntError { ... }).
  6. .ok() changes that error into None.
  7. filter_map receives None, so it skips this item.
  8. For "21", parsing produces Some(21) after .ok(), so 21 is kept.
  9. collect() gathers the kept values into vec![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 None when 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.

Take Quiz

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

ApproachInput behaviorOutput / effectBest for
result.ok()Converts Ok and ErrSome(value) or None; error is discardedOptional or disposable failures
result.err()Converts Ok and ErrSome(error) or None; value is discardedCollecting or inspecting failures
`filter_map(xx.parse().ok())`Keeps successful parses
`map(xx.parse())thencollect::<Result<Vec<_>, _>>()`Stops on an error
match resultHandles both cases explicitlyAny behavior you defineLogging, recovery, or different error policies
unwrap_or(default)Replaces failuresAlways produces a valueA 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.

Take Quiz

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_map expects Option, not Result.
  • Use collect::<Result<Vec<_>, _>>() when any failure should be returned.
  • Use match when 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.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more