047 — Result Bind (and_then)
Tutorial Video
Text description (accessibility)
This video demonstrates the "047 — Result Bind (and_then)" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. `Result::and_then` (bind) sequences two fallible operations: apply a function that returns `Result` to the `Ok` value, propagating `Err` automatically. Key difference from OCaml: 1. **`?` vs `let*`**: Rust's `?` is built into the language. OCaml's `let*` requires the `ppx_let` preprocessor. The `?` operator is more ergonomic for long chains.
Tutorial
The Problem
Result::and_then (bind) sequences two fallible operations: apply a function that returns Result to the Ok value, propagating Err automatically. It is the monadic sequencing operation for Result — "do this, then do that, stopping at the first failure". This enables clean pipelines of fallible operations without nested match statements.
The classic example is a multi-step pipeline: validate input → parse → compute → format. Each step may fail; if any fails, the pipeline stops and propagates the error. Without and_then, this requires nested match blocks or repeated null-checking. With and_then, the happy path reads linearly.
🎯 Learning Outcomes
result.and_then(|v| fallible_transform(v)) to chain fallible operationsmap: and_then applies a function returning Resultand_then as the result monad's bind operationand_then to the ? operator: f()? desugars to an and_then chainResult::and_then(f) to sequence fallible operations — each step feeds its success value to the next? operator as shorthand for and_then-based error propagation in functions returning ResultCode Example
#![allow(clippy::all)]
// Placeholder — pending conversionKey Differences
? vs let***: Rust's ? is built into the language. OCaml's let* requires the ppx_let preprocessor. The ? operator is more ergonomic for long chains.? operator can automatically convert error types via the From trait — ? on Result<T, IoError> in a function returning Result<T, AppError> calls AppError::from(e). OCaml requires explicit Result.map_error.and_then vs >>=**: Haskell uses >>= (pronounced "bind"). Rust calls it and_then. OCaml calls it bind. All are the same operation.and_then only threads through the success channel. Use or_else for the error channel: result.or_else(|e| fallback(e)).and_then sequences fallible steps:** Each step in the chain may fail. If any step returns Err, the chain short-circuits and propagates the error. Only Ok values proceed to the next step.Ok is the happy track; Err is the error track. and_then switches you to the error track on failure — you never switch back without explicit or_else.? operator shorthand:** let x = fallible_op()? is syntactic sugar for and_then in functions returning Result. The ? desugars to match fallible_op() { Ok(v) => v, Err(e) => return Err(e.into()) }.let*:** With ppx_let, let* x = fallible_op () in next_step x is the OCaml equivalent of fallible_op()?.and_then(|x| next_step(x)).OCaml Approach
OCaml's Result.bind r f: let bind r f = match r with Ok x -> f x | Error e -> Error e. Pipe style: parse_int s |> Result.bind safe_div |> Result.bind (fun n -> Ok (string_of_int n)). With let* (ppx_let): let* n = parse_int s in let* d = safe_div n 2 in Ok (string_of_int d) — this reads like imperative code with automatic error propagation.
Full Source
#![allow(clippy::all)]
// Placeholder — pending conversionDeep Comparison
OCaml vs Rust: Result Bind
Overview
See the example.rs and example.ml files for detailed implementations.
Key Differences
| Aspect | OCaml | Rust |
|---|---|---|
| Type system | Hindley-Milner | Ownership + traits |
| Memory | GC | Zero-cost abstractions |
| Mutability | Explicit ref | mut keyword |
| Error handling | Option/Result | Result<T, E> |
See README.md for detailed comparison.
Exercises
"host:port", splits on :, parses the port as u16, and validates the host is non-empty. Use and_then at each step.find_user(id).and_then(|u| find_posts(u.id)).and_then(|posts| render(posts)) with stub functions. Handle the case where each step might return Err("not found").and_then chain using (a) nested match, (b) and_then, (c) ? operator. Verify all three produce the same results. Discuss readability.pipeline(input: &str) -> Result<f64, String> that parses a string to integer, checks it's positive, then computes its square root — using three chained and_then calls.? operator**: Rewrite the same pipeline function using the ? operator instead of explicit and_then chains. Both must produce identical behavior.