liveBook · Manning

Result type

As explained in Refactoring to Rust

Result is Rust's standard-library enum representing either success with an Ok value or failure with an Err value.

A function that might fail can return `Result<T, E>` instead of mixing errors into its normal output. This standard-library enum holds either `Ok(T)`, a successful value, or `Err(E)`, an error value. For example, a FizzBuzz function can return its usual FizzBuzz value for valid input and an error for a negative number. section 2.5.2

The caller must account for both possibilities. A `match` can use the value inside `Ok` or handle `Err`. A function that only prints on success can return `Result<(), Error>`: `()` is the unit type, used when there is no success value to return. section 2.5.2 section 2.6

When a function should pass an error to its caller, `?` shortens a `match` with an early return. On `Ok`, the expression yields the contained value; on `Err`, it returns the error immediately. Error types can also be specific: an `Error::GotNegative` variant gives the caller a case to handle. section 2.5.4

Listing 2.34 Example use of the ? operator
fn foo(i: i32) -> Result<FizzBuzzValue, Error> {
  let result = match fizzbuzz(i) {  #1
    Ok(x) => {
      x
    }
    Err(e) => {
      return Err(e);
    }
  };

  println!("{} is a valid number for fizzbuzz", i);

  Ok(result);
}


fn bar(i: i32) -> Result<FizzBuzzValue, Error> {
  let result = fizzbuzz(i)?;  #2

  println!("{} is a valid number for fizzbuzz", i);  #3

  Ok(result);
}

Compare the explicit `match` and the shorter `?` form for forwarding a FizzBuzz error. section 2.5.4

Giving validation failures useful names

Suppose lowercase and uniqueness checks each return `Result<(), ()>`. A username validator needs to distinguish their failures. Applying `map_err` to each result converts its uninformative `Err(())` into `UsernameError::NotLowercase` or `UsernameError::NotUnique`; `?` then forwards the converted error. Successful checks continue to the final `Ok(())`. section 2.5.5

Listing 2.36 Program that validates usernames
enum UsernameError {
  NotLowercase,
  NotUnique,
}

fn main() {
  match validate_username("user1") {
    Ok(()) => println!("Valid username"),
    Err(UsernameError::NotLowercase) => println!(
      "Username must be lowercase"),
    Err(UsernameError::NotUnique) => println!(
      "Username already exists"),
  }
}

fn validate_username(username: &str) -> Result<(), UsernameError>
{
  validate_lowercase(username).map_err(
    |_| UsernameError::NotLowercase)?;
  validate_unique(username).map_err(
    |_| UsernameError::NotUnique)?;

  Ok(())
}

fn validate_lowercase(username: &str) -> Result<(), ()> {
  Ok(()) #1
}

fn validate_unique(username: &str) -> Result<(), ()> {
  Ok(())
}

See the complete validator and the caller’s separate handling of its two error variants. section 2.5.5

How Refactoring to Rust covers it

How deep it goes
Defines both enum variants and works through matching, propagation with `?`, and error conversion with `map_err`.
Examples
Uses negative FizzBuzz input, parallel `match` and `?` functions, and lowercase and uniqueness checks for usernames.
What it assumes
Introduces `Result` through its enum definition and explains the unit type alongside a concrete return signature.
What you can do after
Apply it via Rust’s standard-library `Result` to handle, forward, and transform function errors.
How it connects
Connects enums and pattern matching to custom error types, the unit type, `?`, and `map_err`.

Also explained in

Compare how 8 books explain result type →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Refactoring to Rust. The section links point to the parts of the book it draws on.