315: Result ok() and err() Methods
Tutorial Video
Text description (accessibility)
This video demonstrates the "315: Result ok() and err() Methods" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. The full `Result<T, E>` method surface is large — over 20 methods covering query, transformation, combination, and extraction. Key difference from OCaml: 1. **Method surface**: Rust has ~20+ methods on `Result`; OCaml's standard `Result` module has fewer, with `Base.Result` providing more.
Tutorial
The Problem
The full Result<T, E> method surface is large — over 20 methods covering query, transformation, combination, and extraction. Many Rust developers use only ?, unwrap(), and map(), missing more specialized tools like ok() (discard error, get Option<T>), err() (discard success, get Option<E>), map_or(), and and()/or(). This reference covers the complete method set and their appropriate use cases.
🎯 Learning Outcomes
ok() to convert Result<T, E> to Option<T> discarding the errorerr() to convert Result<T, E> to Option<E> discarding the successmap_or(default, f) and map_or_else(err_f, ok_f) for default valuesand(other) and or(other) for boolean-like result chainingCode Example
#![allow(clippy::all)]
//! # Exhaustive Result/Option Method Survey
//!
//! Complete reference of `Result<T,E>` and `Option<T>` methods.
/// Demonstrate Result methods
pub fn result_methods() {
let ok: Result<i32, &str> = Ok(5);
let err: Result<i32, &str> = Err("bad");
// Query methods
let _ = ok.is_ok();
let _ = ok.is_err();
let _ = ok.ok();
let _ = err.err();
// Transform methods
let _ = ok.map(|x| x * 2);
let _ = err.map_err(|e| format!("error: {}", e));
let _ = ok.map_or(0, |x| x + 1);
let _ = ok.map_or_else(|_| 0, |x| x);
// Combinators
let _ = ok.and(Ok::<i32, &str>(10));
let _ = err.or(Ok::<i32, &str>(42));
let _ = ok.and_then(|x| Ok::<i32, &str>(x * 2));
let _ = err.or_else(|_| Ok::<i32, &str>(99));
// Unwrap variants
let _ = ok.unwrap_or(0);
let _ = ok.unwrap_or_else(|_| 0);
let _ = ok.unwrap_or_default();
}
/// Demonstrate Option methods
pub fn option_methods() {
let some: Option<i32> = Some(5);
let none: Option<i32> = None;
// Query methods
let _ = some.is_some();
let _ = none.is_none();
// Transform methods
let _ = some.map(|x| x * 2);
let _ = some.filter(|&x| x > 3);
let _ = some.map_or(0, |x| x + 1);
// Combinators
let _ = some.and(Some(10));
let _ = none.or(Some(42));
let _ = some.and_then(|x| Some(x * 2));
let _ = none.or_else(|| Some(99));
// Unwrap variants
let _ = none.unwrap_or(0);
let _ = none.unwrap_or_else(|| 99);
let _ = none.unwrap_or_default();
// Conversion
let _ = none.ok_or("missing");
let _ = Some(Some(42)).flatten();
let _ = some.zip(Some("hello"));
}
#[cfg(test)]
mod tests {
#[test]
fn test_result_map_chain() {
let r: Result<i32, &str> = Ok(5);
assert_eq!(r.map(|x| x * 2).map(|x| x + 1), Ok(11));
}
#[test]
fn test_option_and_then_chain() {
let r = Some(5i32)
.and_then(|x| if x > 0 { Some(x * 2) } else { None })
.filter(|&x| x < 20);
assert_eq!(r, Some(10));
}
#[test]
fn test_result_or_else() {
let r: Result<i32, &str> = Err("bad");
assert_eq!(r.or_else(|_| Ok::<i32, &str>(42)), Ok(42));
}
#[test]
fn test_option_zip() {
assert_eq!(Some(1).zip(Some("a")), Some((1, "a")));
assert_eq!(Some(1).zip(None::<&str>), None);
}
#[test]
fn test_option_flatten() {
assert_eq!(Some(Some(42)).flatten(), Some(42));
assert_eq!(Some(None::<i32>).flatten(), None);
}
}Key Differences
Result; OCaml's standard Result module has fewer, with Base.Result providing more.ok() as filter**: result.ok() is the idiomatic way to silently drop errors when only the success case matters — used extensively in filter_map.and/or logic**: and(other) propagates Err from self; or(other) propagates Ok from self — they model error "and"/"or" logic.match — a map_or is cleaner than a match with two branches.OCaml Approach
OCaml's Result module provides Result.is_ok, Result.is_error, Result.map, Result.map_error, and Result.fold — a subset of Rust's methods. Result.ok (convert to Option) exists as Result.to_option:
Result.is_ok (Ok 5) (* true *)
Result.to_option (Ok 5) (* Some 5 *)
Result.fold ~ok:(fun x -> x+1) ~error:(fun _ -> 0) (Ok 5) (* 6 *)
Full Source
#![allow(clippy::all)]
//! # Exhaustive Result/Option Method Survey
//!
//! Complete reference of `Result<T,E>` and `Option<T>` methods.
/// Demonstrate Result methods
pub fn result_methods() {
let ok: Result<i32, &str> = Ok(5);
let err: Result<i32, &str> = Err("bad");
// Query methods
let _ = ok.is_ok();
let _ = ok.is_err();
let _ = ok.ok();
let _ = err.err();
// Transform methods
let _ = ok.map(|x| x * 2);
let _ = err.map_err(|e| format!("error: {}", e));
let _ = ok.map_or(0, |x| x + 1);
let _ = ok.map_or_else(|_| 0, |x| x);
// Combinators
let _ = ok.and(Ok::<i32, &str>(10));
let _ = err.or(Ok::<i32, &str>(42));
let _ = ok.and_then(|x| Ok::<i32, &str>(x * 2));
let _ = err.or_else(|_| Ok::<i32, &str>(99));
// Unwrap variants
let _ = ok.unwrap_or(0);
let _ = ok.unwrap_or_else(|_| 0);
let _ = ok.unwrap_or_default();
}
/// Demonstrate Option methods
pub fn option_methods() {
let some: Option<i32> = Some(5);
let none: Option<i32> = None;
// Query methods
let _ = some.is_some();
let _ = none.is_none();
// Transform methods
let _ = some.map(|x| x * 2);
let _ = some.filter(|&x| x > 3);
let _ = some.map_or(0, |x| x + 1);
// Combinators
let _ = some.and(Some(10));
let _ = none.or(Some(42));
let _ = some.and_then(|x| Some(x * 2));
let _ = none.or_else(|| Some(99));
// Unwrap variants
let _ = none.unwrap_or(0);
let _ = none.unwrap_or_else(|| 99);
let _ = none.unwrap_or_default();
// Conversion
let _ = none.ok_or("missing");
let _ = Some(Some(42)).flatten();
let _ = some.zip(Some("hello"));
}
#[cfg(test)]
mod tests {
#[test]
fn test_result_map_chain() {
let r: Result<i32, &str> = Ok(5);
assert_eq!(r.map(|x| x * 2).map(|x| x + 1), Ok(11));
}
#[test]
fn test_option_and_then_chain() {
let r = Some(5i32)
.and_then(|x| if x > 0 { Some(x * 2) } else { None })
.filter(|&x| x < 20);
assert_eq!(r, Some(10));
}
#[test]
fn test_result_or_else() {
let r: Result<i32, &str> = Err("bad");
assert_eq!(r.or_else(|_| Ok::<i32, &str>(42)), Ok(42));
}
#[test]
fn test_option_zip() {
assert_eq!(Some(1).zip(Some("a")), Some((1, "a")));
assert_eq!(Some(1).zip(None::<&str>), None);
}
#[test]
fn test_option_flatten() {
assert_eq!(Some(Some(42)).flatten(), Some(42));
assert_eq!(Some(None::<i32>).flatten(), None);
}
}#[cfg(test)]
mod tests {
#[test]
fn test_result_map_chain() {
let r: Result<i32, &str> = Ok(5);
assert_eq!(r.map(|x| x * 2).map(|x| x + 1), Ok(11));
}
#[test]
fn test_option_and_then_chain() {
let r = Some(5i32)
.and_then(|x| if x > 0 { Some(x * 2) } else { None })
.filter(|&x| x < 20);
assert_eq!(r, Some(10));
}
#[test]
fn test_result_or_else() {
let r: Result<i32, &str> = Err("bad");
assert_eq!(r.or_else(|_| Ok::<i32, &str>(42)), Ok(42));
}
#[test]
fn test_option_zip() {
assert_eq!(Some(1).zip(Some("a")), Some((1, "a")));
assert_eq!(Some(1).zip(None::<&str>), None);
}
#[test]
fn test_option_flatten() {
assert_eq!(Some(Some(42)).flatten(), Some(42));
assert_eq!(Some(None::<i32>).flatten(), None);
}
}
Deep Comparison
result-ok-err-methods
See README.md for details.
Exercises
ok() and filter_map() together to collect only successfully parsed values from a Vec<Result<i32, _>>.and() to chain two Result values: only proceed if both are Ok, return the first Err otherwise.map_or_else(|e| log_and_default(e), |v| v) to log errors and provide fallback values in a single expression.