πŸ¦€ Functional Rust
🎬 Error Handling in Rust Option, Result, the ? operator, and combinators.
πŸ“ Text version (for readers / accessibility)

β€’ Option represents a value that may or may not exist β€” Some(value) or None

β€’ Result represents success (Ok) or failure (Err) β€” no exceptions needed

β€’ The ? operator propagates errors up the call stack concisely

β€’ Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations

β€’ The compiler forces you to handle every error case β€” no silent failures

045: Result Basics

Difficulty: 1 Level: Foundations Understand `Result<T, E>` β€” Rust's way of making errors visible, explicit, and impossible to ignore.

The Problem This Solves

You're writing a function that divides two numbers. In Python you'd write `a / b` and maybe wrap it in `try/except ZeroDivisionError`. But here's the thing: nothing in Python's type system tells you that function can fail. A caller reading your code has no way to know β€” they have to read the docs, or discover it at runtime when the exception bubbles up. This is the "hidden failure" problem. In large codebases with many layers, exceptions silently skip the call stack. A Java method marked `throws SomeException` is closer to the truth, but still opt-in β€” the compiler won't stop you from ignoring it. You can always just… not catch it. Rust takes a different approach. A function that can fail returns `Result<T, E>`. If it returns `Ok(value)`, the call succeeded. If it returns `Err(error)`, it failed. The caller must decide what to do β€” the compiler enforces it. You literally cannot accidentally ignore an error. This isn't a style preference; it's baked into the type system.

The Intuition

Think about Python's `try/except`. It works, but the failure mode is invisible: you call a function, have no idea if it throws, and find out at runtime. JavaScript's Promises are better β€” `.then()` makes the async path explicit β€” but regular functions still throw silently. In Rust, `Result<i64, MathError>` in the return type is a contract: "this function either gives you an `i64`, or it gives you a `MathError`. Handle both." The key insight: in Rust, the type signature tells you whether a function can fail β€” no surprises. You match on it just like a Python `if/else`, but the compiler won't let you forget the error case:
match safe_div(10, 0) {
 Ok(value) => println!("Got: {}", value),
 Err(e)    => println!("Failed: {:?}", e),
}

How It Works in Rust

// Define what errors your function can produce
#[derive(Debug, PartialEq)]
enum MathError {
 DivisionByZero,
 NegativeSquareRoot,
}

// Return type says: "this can fail with MathError"
fn safe_div(a: i64, b: i64) -> Result<i64, MathError> {
 if b == 0 {
     Err(MathError::DivisionByZero)   // wrap error in Err(...)
 } else {
     Ok(a / b)                         // wrap success in Ok(...)
 }
}

// Pattern match to handle both cases
match safe_div(10, 2) {
 Ok(v)  => println!("{}", v),          // prints 5
 Err(e) => println!("{:?}", e),
}

// Utility methods for quick access
safe_div(10, 2).unwrap_or(0);            // returns 5, or 0 if Err
safe_div(10, 0).is_err();                // true
safe_div(10, 2).ok();                    // Some(5) β€” converts to Option

What This Unlocks

Key Differences

ConceptOCamlRust
Success value`Ok value``Ok(value)`
Failure value`Error e``Err(e)`
Pattern match`match r with Ok v -> ... \Error e -> ...``match r { Ok(v) => ..., Err(e) => ... }`
Error typeAny type, often polymorphic `('a, 'b) result`Must be concrete: `Result<T, E>`
Ignoring errorsCompiler warns, but possibleCompiler error (unused `Result` triggers warning that's hard to ignore)
Convert to Option`Result.to_option``.ok()`
Default on error`Result.value ~default``.unwrap_or(default)`
// Result Basics β€” 99 Problems #45
// Explore Result<T, E>: construction, matching, ok/err conversion.

#[derive(Debug, PartialEq)]
enum MathError {
    DivisionByZero,
    NegativeSquareRoot,
    Overflow,
}

fn safe_div(a: i64, b: i64) -> Result<i64, MathError> {
    if b == 0 {
        Err(MathError::DivisionByZero)
    } else {
        Ok(a / b)
    }
}

fn safe_sqrt(x: f64) -> Result<f64, MathError> {
    if x < 0.0 {
        Err(MathError::NegativeSquareRoot)
    } else {
        Ok(x.sqrt())
    }
}

fn parse_int(s: &str) -> Result<i64, String> {
    s.trim().parse::<i64>().map_err(|e| format!("parse error: {}", e))
}

fn main() {
    // Basic Ok/Err
    println!("safe_div(10, 2)  = {:?}", safe_div(10, 2));
    println!("safe_div(10, 0)  = {:?}", safe_div(10, 0));
    println!("safe_sqrt(9.0)   = {:?}", safe_sqrt(9.0));
    println!("safe_sqrt(-1.0)  = {:?}", safe_sqrt(-1.0));
    println!("parse_int('42')  = {:?}", parse_int("42"));
    println!("parse_int('abc') = {:?}", parse_int("abc"));

    // Pattern matching
    match safe_div(100, 4) {
        Ok(v) => println!("100/4 = {}", v),
        Err(e) => println!("Error: {:?}", e),
    }

    // ok() converts to Option
    println!("ok():            {:?}", safe_div(10, 2).ok());
    println!("ok() err:        {:?}", safe_div(10, 0).ok());

    // err() extracts the error as Option
    println!("err():           {:?}", safe_div(10, 0).err());

    // is_ok / is_err
    println!("is_ok(10/2):     {}", safe_div(10, 2).is_ok());
    println!("is_err(10/0):    {}", safe_div(10, 0).is_err());

    // unwrap_or
    println!("unwrap_or(0):    {}", safe_div(10, 0).unwrap_or(0));

    // unwrap_or_else
    println!("unwrap_or_else:  {}", safe_div(10, 0).unwrap_or_else(|_| -1));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_ok() {
        assert_eq!(safe_div(10, 2), Ok(5));
    }

    #[test]
    fn test_err() {
        assert_eq!(safe_div(10, 0), Err(MathError::DivisionByZero));
    }

    #[test]
    fn test_sqrt_ok() {
        assert_eq!(safe_sqrt(4.0), Ok(2.0));
    }

    #[test]
    fn test_sqrt_err() {
        assert_eq!(safe_sqrt(-1.0), Err(MathError::NegativeSquareRoot));
    }

    #[test]
    fn test_ok_to_option() {
        assert_eq!(safe_div(10, 2).ok(), Some(5));
        assert_eq!(safe_div(10, 0).ok(), None);
    }

    #[test]
    fn test_parse() {
        assert!(parse_int("42").is_ok());
        assert!(parse_int("abc").is_err());
    }
}
(* Result Basics *)
(* OCaml 99 Problems #45 *)

(* Implementation for example 45 *)

(* Tests *)
let () =
  (* Add tests *)
  print_endline "βœ“ OCaml tests passed"