ExamplesBy LevelBy TopicLearning Paths
315 Intermediate

315: Result ok() and err() Methods

Functional Programming

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

  • • Use ok() to convert Result<T, E> to Option<T> discarding the error
  • • Use err() to convert Result<T, E> to Option<E> discarding the success
  • • Understand map_or(default, f) and map_or_else(err_f, ok_f) for default values
  • • Use and(other) and or(other) for boolean-like result chaining
  • Code 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

  • Method surface: Rust has ~20+ methods on 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.
  • Exhaustive coverage: Knowing all methods prevents reinventing them with 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);
        }
    }
    ✓ Tests Rust test suite
    #[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

  • Use ok() and filter_map() together to collect only successfully parsed values from a Vec<Result<i32, _>>.
  • Use and() to chain two Result values: only proceed if both are Ok, return the first Err otherwise.
  • Use map_or_else(|e| log_and_default(e), |v| v) to log errors and provide fallback values in a single expression.
  • Open Source Repos