045 — Result Basics
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
Result<T, E> with Ok(value) and Err(error)match to handle both success and error cases.is_ok(), .is_err(), .unwrap(), .unwrap_or(default) for common patternsOption (no value vs value) and Result (success vs error with info).ok() to convert Result<T,E> to Option<T> when the error type does not matterResult<T, E> to represent operations that can fail with a typed error — avoiding sentinel values and exceptionsexpect("message") over unwrap() to provide context when panicking in tests or when Err is impossibleCode Example
#![allow(clippy::all)]
// Placeholder — pending conversionKey Differences
Err vs Error**: Rust uses Err(e) for the error variant; OCaml uses Error e. This is purely a naming difference.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 Result — let x = fallible()?; returns Err(e) early if the function fails. OCaml needs let* syntax with ppx_let or explicit match.Not_found from List.find); functional OCaml code prefers result. Rust has no exceptions at all — Result is the only mechanism.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.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 conversionDeep Comparison
OCaml vs Rust: Result Basics
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
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.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.parse_all(ss: &[&str]) -> Result<Vec<i32>, String> that parses all strings, returning Err with the first parse failure. Use .collect::<Result<Vec<_>, _>>().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.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.