ExamplesBy LevelBy TopicLearning Paths
045 Intermediate

045 — Result Basics

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "045 — Result Basics" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. `Result<T, E>` extends `Option<T>` with error information: instead of `None`, a failure produces `Err(e)` carrying a typed error value `e`. Key difference from OCaml: 1. **`Err` vs `Error`**: Rust uses `Err(e)` for the error variant; OCaml uses `Error e`. This is purely a naming difference.

Tutorial

The Problem

Result<T, E> extends Option<T> with error information: instead of None, a failure produces Err(e) carrying a typed error value e. This is how Rust handles all recoverable errors — file I/O, network calls, parsing, validation — without exceptions or error codes. The type system forces callers to handle both Ok(value) and Err(error) cases.

Result originates from Haskell's Either type and OCaml's result type. Unlike exceptions (which are invisible in type signatures), Result makes the possibility of failure explicit: a function returning Result<i32, ParseError> clearly communicates "this can fail with a ParseError". This is the basis of Rust's famous "fearless error handling".

🎯 Learning Outcomes

  • • Construct and destructure Result<T, E> with Ok(value) and Err(error)
  • • Use match to handle both success and error cases
  • • Use .is_ok(), .is_err(), .unwrap(), .unwrap_or(default) for common patterns
  • • Understand the difference between Option (no value vs value) and Result (success vs error with info)
  • • Use .ok() to convert Result<T,E> to Option<T> when the error type does not matter
  • • Use Result<T, E> to represent operations that can fail with a typed error — avoiding sentinel values and exceptions
  • • Use expect("message") over unwrap() to provide context when panicking in tests or when Err is impossible
  • Code Example

    #![allow(clippy::all)]
    // Placeholder — pending conversion

    Key Differences

  • **Err vs Error**: Rust uses Err(e) for the error variant; OCaml uses Error e. This is purely a naming difference.
  • Generic error type: Rust's Result<T, E> is generic over both the success and error types. OCaml's ('a, 'b) result is the same. This means errors can be strings, enums, custom structs, or any type.
  • **? propagation**: Rust's ? operator works on Resultlet x = fallible()?; returns Err(e) early if the function fails. OCaml needs let* syntax with ppx_let or explicit match.
  • Exception vs Result: OCaml code often uses exceptions for errors (e.g., Not_found from List.find); functional OCaml code prefers result. Rust has no exceptions at all — Result is the only mechanism.
  • Typed errors: Result<T, E> carries a typed error E. This forces the caller to decide how to handle each specific error case. Option only distinguishes "value" from "no value" — Result also says "why there's no value."
  • **match for exhaustive handling:** A match on Result must handle both Ok(v) and Err(e) — the compiler enforces this. The error is not silently ignored.
  • **expect(msg) for context:** result.expect("context message") panics with a descriptive message if the result is Err. Prefer expect over unwrap so panics include useful context.
  • **OCaml result type:** type ('a, 'b) result = Ok of 'a | Error of 'b. Usage identical to Rust. OCaml 4.03+ has Ok and Error constructors in scope by default.
  • OCaml Approach

    OCaml's result type: type ('a, 'b) result = Ok of 'a | Error of 'b. (Note: OCaml uses Error, not Err.) Usage: match r with Ok v -> ... | Error e -> .... Result.value r ~default:x returns the value or default. Result.get_ok r panics on Error. Result.is_ok r and Result.is_error r are predicates. Result.to_option r converts to option.

    Full Source

    #![allow(clippy::all)]
    // Placeholder — pending conversion

    Deep Comparison

    OCaml vs Rust: Result Basics

    Overview

    See the example.rs and example.ml files for detailed implementations.

    Key Differences

    AspectOCamlRust
    Type systemHindley-MilnerOwnership + traits
    MemoryGCZero-cost abstractions
    MutabilityExplicit refmut keyword
    Error handlingOption/ResultResult<T, E>

    See README.md for detailed comparison.

    Exercises

  • Integer parse: Write parse_positive(s: &str) -> Result<u32, String> that parses a string to a positive integer, returning descriptive error messages for non-integer and negative inputs.
  • Result combination: Write add_results(a: Result<i32, String>, b: Result<i32, String>) -> Result<i32, String> that adds the values if both are Ok, or returns the first error.
  • Collect results: Write parse_all(ss: &[&str]) -> Result<Vec<i32>, String> that parses all strings, returning Err with the first parse failure. Use .collect::<Result<Vec<_>, _>>().
  • Chain of validations: Write validate_age(input: &str) -> Result<u8, String> that parses a string to a number and checks it's between 0 and 150, returning descriptive errors for each failure mode.
  • Result to Option: Write ok(result: Result<T, E>) -> Option<T> that discards the error and returns Some(v) or None. Then write err(result: Result<T, E>) -> Option<E> for the error side.
  • Open Source Repos