๐Ÿฆ€ 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

047: Result Bind

Difficulty: 1 Level: Foundations Chain multiple fallible operations cleanly โ€” stop at the first failure, with zero boilerplate.

The Problem This Solves

Real programs rarely do one thing. You parse input, divide it, check a range, verify a constraint โ€” four steps, any of which can fail. In Python you'd write four `try/except` blocks or deeply nested `if err != None` checks. The signal (your actual business logic) gets buried in the noise (error handling). In JavaScript, Promise chaining solves this for async code: `.then().then().then()` with a single `.catch()` at the end. Each step gets the previous step's value; if any step rejects, the rest are skipped. It's clean. `and_then` (also called `bind` or `flatMap` in functional programming) brings that same clarity to synchronous Rust code. Each step in the chain only runs if the previous step succeeded. The first `Err` short-circuits everything. Your business logic reads top-to-bottom without nesting.

The Intuition

`and_then` is the answer to: "what if the next operation can also fail?" Compare: Think of `and_then` as "if Ok, try this next thing." If it also succeeds, keep going. If it fails, stop and return that error. The chain collapses the moment anything goes wrong โ€” no nested match, no intermediate variable checking. The `?` operator is syntactic sugar for `and_then` + early return. Both styles produce identical behavior; `?` just reads more like imperative code.

How It Works in Rust

fn parse_int(s: &str) -> Result<i64, Error> { ... }
fn safe_div(a: i64, b: i64) -> Result<i64, Error> { ... }
fn check_positive(x: i64) -> Result<i64, Error> { ... }
fn check_small(x: i64) -> Result<i64, Error> { ... }

// Style 1: and_then chain
// Each step only runs if the previous returned Ok
fn pipeline(input: &str, divisor: i64) -> Result<i64, Error> {
 parse_int(input)
     .and_then(|x| safe_div(x, divisor))  // only runs if parse succeeded
     .and_then(check_positive)              // only runs if div succeeded
     .and_then(check_small)                 // only runs if positive check passed
}

// Style 2: ? operator โ€” equivalent, reads like normal imperative code
fn pipeline_question(input: &str, divisor: i64) -> Result<i64, Error> {
 let x = parse_int(input)?;    // if Err: return that Err immediately
 let y = safe_div(x, divisor)?;
 let z = check_positive(y)?;
 check_small(z)                 // last step: return its Result directly
}

// Both styles produce IDENTICAL results:
pipeline("100", 5)    // Ok(20) โ€” 100/5=20, positive, small
pipeline("100", 0)    // Err(DivByZero) โ€” stops at step 2
pipeline("-100", 5)   // Err(Negative) โ€” stops at step 3
pipeline("5000", 1)   // Err(TooLarge) โ€” stops at step 4
pipeline("abc", 2)    // Err(Parse(...)) โ€” stops at step 1

What This Unlocks

Key Differences

ConceptOCamlRust
Bind operator`>>=` (infix: `r >>= f`)`.and_then(f)` (method)
? equivalent`let x = r in ...` (OCaml 4.08+)`let x = r?;`
Early exit`match` or `let``?` returns from current function
Error threadingSame error type throughoutSame error type, or use `From` for conversion
Function signature`'a result -> ('a -> 'b result) -> 'b result``Result<T,E>` + closure `T -> Result<U,E>`
Method vs function`Result.bind r f``r.and_then(f)`
// Result Bind โ€” 99 Problems #47
// Chain fallible computations with and_then (bind / flatMap).

#[derive(Debug, PartialEq)]
enum Error {
    Parse(String),
    DivByZero,
    Negative,
    TooLarge,
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::Parse(s) => write!(f, "parse error: {}", s),
            Error::DivByZero => write!(f, "division by zero"),
            Error::Negative => write!(f, "negative value"),
            Error::TooLarge => write!(f, "value too large"),
        }
    }
}

fn parse_int(s: &str) -> Result<i64, Error> {
    s.trim().parse::<i64>().map_err(|_| Error::Parse(s.to_string()))
}

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

fn check_positive(x: i64) -> Result<i64, Error> {
    if x > 0 { Ok(x) } else { Err(Error::Negative) }
}

fn check_small(x: i64) -> Result<i64, Error> {
    if x < 1000 { Ok(x) } else { Err(Error::TooLarge) }
}

/// Full pipeline using and_then.
fn pipeline(input: &str, divisor: i64) -> Result<i64, Error> {
    parse_int(input)
        .and_then(|x| safe_div(x, divisor))
        .and_then(check_positive)
        .and_then(check_small)
}

/// Same using the ? operator.
fn pipeline_question(input: &str, divisor: i64) -> Result<i64, Error> {
    let x = parse_int(input)?;
    let y = safe_div(x, divisor)?;
    let z = check_positive(y)?;
    check_small(z)
}

fn main() {
    let cases = [
        ("100", 5),
        ("100", 0),
        ("-100", 5),
        ("5000", 1),
        ("abc", 2),
    ];

    for (s, d) in &cases {
        let r = pipeline(s, *d);
        println!("pipeline({:?}, {}) = {:?}", s, d, r);
    }

    println!("\n? operator version:");
    for (s, d) in &cases {
        let r = pipeline_question(s, *d);
        println!("pipeline_q({:?}, {}) = {:?}", s, d, r);
    }
}

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

    #[test]
    fn test_success() {
        assert_eq!(pipeline("100", 5), Ok(20));
    }

    #[test]
    fn test_div_zero() {
        assert_eq!(pipeline("100", 0), Err(Error::DivByZero));
    }

    #[test]
    fn test_negative_result() {
        assert_eq!(pipeline("-100", 5), Err(Error::Negative));
    }

    #[test]
    fn test_too_large() {
        assert_eq!(pipeline("5000", 1), Err(Error::TooLarge));
    }

    #[test]
    fn test_question_matches_and_then() {
        for (s, d) in [("100", 5), ("abc", 1), ("-10", 2)] {
            assert_eq!(pipeline(s, d), pipeline_question(s, d));
        }
    }
}
(* Result Bind *)
(* OCaml 99 Problems #47 *)

(* Implementation for example 47 *)

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