ExamplesBy LevelBy TopicLearning Paths
561 Fundamental

Or Patterns

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "Or Patterns" functional Rust example. Difficulty level: Fundamental. Key concepts covered: Functional Programming. Matching the same action for multiple variants is a common need in language interpreters, state machines, and data validation. Key difference from OCaml: 1. **Stabilization history**: OCaml has had or

Tutorial

The Problem

Matching the same action for multiple variants is a common need in language interpreters, state machines, and data validation. Before Rust 2021's or-pattern stabilization, developers had to either duplicate arms or use if matches!(...) guards. Or patterns (| in match arms and if let) allow matching several alternatives with one arm, keeping code DRY and exhaustiveness checking intact. OCaml has always had this with its | in match, making it a natural comparison point.

🎯 Learning Outcomes

  • • How | in a single match arm matches multiple alternatives
  • • How matches!(value, A | B | C) provides a concise boolean check
  • • How or-patterns work in if let and let destructuring
  • • How or-patterns interact with enum variants, ranges, and literals
  • • Where or-patterns reduce duplication in state machine transitions and input validation
  • Code Example

    #![allow(clippy::all)]
    //! Or Patterns
    //!
    //! Matching multiple alternatives with |.
    
    /// Match multiple values.
    pub fn is_vowel(c: char) -> bool {
        matches!(c, 'a' | 'e' | 'i' | 'o' | 'u' | 'A' | 'E' | 'I' | 'O' | 'U')
    }
    
    /// Or pattern in match.
    pub fn describe_number(n: i32) -> &'static str {
        match n {
            0 => "zero",
            1 | 2 | 3 => "small",
            4 | 5 | 6 => "medium",
            7 | 8 | 9 => "large",
            _ => "huge",
        }
    }
    
    /// Or in enum matching.
    #[derive(Debug)]
    pub enum Color {
        Red,
        Green,
        Blue,
        Yellow,
        Cyan,
        Magenta,
    }
    
    pub fn is_primary(c: &Color) -> bool {
        matches!(c, Color::Red | Color::Green | Color::Blue)
    }
    
    /// Or with bindings (same name in each arm).
    pub fn extract_value(opt: Option<Result<i32, i32>>) -> Option<i32> {
        match opt {
            Some(Ok(v) | Err(v)) => Some(v),
            None => None,
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_vowel() {
            assert!(is_vowel('a'));
            assert!(!is_vowel('b'));
        }
    
        #[test]
        fn test_describe() {
            assert_eq!(describe_number(0), "zero");
            assert_eq!(describe_number(2), "small");
            assert_eq!(describe_number(5), "medium");
        }
    
        #[test]
        fn test_primary() {
            assert!(is_primary(&Color::Red));
            assert!(!is_primary(&Color::Yellow));
        }
    
        #[test]
        fn test_extract() {
            assert_eq!(extract_value(Some(Ok(5))), Some(5));
            assert_eq!(extract_value(Some(Err(3))), Some(3));
        }
    }

    Key Differences

  • Stabilization history: OCaml has had or-patterns since v1; Rust stabilized them fully in edition 2021 — earlier editions required separate arms.
  • Scope of binding: Rust requires all alternatives in an or-pattern to bind the same names with the same types; OCaml has the same restriction.
  • **matches! macro**: Rust's matches! is a convenient shorthand; OCaml achieves the same with a function | Pat1 | Pat2 -> true | _ -> false.
  • Nested or-patterns: Rust supports nested or-patterns inside destructuring (Some(1 | 2)); OCaml supports the same.
  • OCaml Approach

    OCaml has had or-patterns since its earliest versions:

    let is_vowel c = match c with
      | 'a' | 'e' | 'i' | 'o' | 'u' -> true
      | _ -> false
    
    let describe_number n = match n with
      | 1 | 2 | 3 -> "small"
      | 4 | 5 | 6 -> "medium"
      | _ -> "other"
    

    The syntax is identical in spirit to Rust's.

    Full Source

    #![allow(clippy::all)]
    //! Or Patterns
    //!
    //! Matching multiple alternatives with |.
    
    /// Match multiple values.
    pub fn is_vowel(c: char) -> bool {
        matches!(c, 'a' | 'e' | 'i' | 'o' | 'u' | 'A' | 'E' | 'I' | 'O' | 'U')
    }
    
    /// Or pattern in match.
    pub fn describe_number(n: i32) -> &'static str {
        match n {
            0 => "zero",
            1 | 2 | 3 => "small",
            4 | 5 | 6 => "medium",
            7 | 8 | 9 => "large",
            _ => "huge",
        }
    }
    
    /// Or in enum matching.
    #[derive(Debug)]
    pub enum Color {
        Red,
        Green,
        Blue,
        Yellow,
        Cyan,
        Magenta,
    }
    
    pub fn is_primary(c: &Color) -> bool {
        matches!(c, Color::Red | Color::Green | Color::Blue)
    }
    
    /// Or with bindings (same name in each arm).
    pub fn extract_value(opt: Option<Result<i32, i32>>) -> Option<i32> {
        match opt {
            Some(Ok(v) | Err(v)) => Some(v),
            None => None,
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_vowel() {
            assert!(is_vowel('a'));
            assert!(!is_vowel('b'));
        }
    
        #[test]
        fn test_describe() {
            assert_eq!(describe_number(0), "zero");
            assert_eq!(describe_number(2), "small");
            assert_eq!(describe_number(5), "medium");
        }
    
        #[test]
        fn test_primary() {
            assert!(is_primary(&Color::Red));
            assert!(!is_primary(&Color::Yellow));
        }
    
        #[test]
        fn test_extract() {
            assert_eq!(extract_value(Some(Ok(5))), Some(5));
            assert_eq!(extract_value(Some(Err(3))), Some(3));
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_vowel() {
            assert!(is_vowel('a'));
            assert!(!is_vowel('b'));
        }
    
        #[test]
        fn test_describe() {
            assert_eq!(describe_number(0), "zero");
            assert_eq!(describe_number(2), "small");
            assert_eq!(describe_number(5), "medium");
        }
    
        #[test]
        fn test_primary() {
            assert!(is_primary(&Color::Red));
            assert!(!is_primary(&Color::Yellow));
        }
    
        #[test]
        fn test_extract() {
            assert_eq!(extract_value(Some(Ok(5))), Some(5));
            assert_eq!(extract_value(Some(Err(3))), Some(3));
        }
    }

    Deep Comparison

    OCaml vs Rust: pattern or

    See example.rs and example.ml for implementations.

    Exercises

  • Operator classifier: Write fn is_arithmetic_op(c: char) -> bool using or-patterns to check for +, -, *, /, %.
  • Status grouping: Implement fn http_category(code: u16) -> &'static str using or-patterns and ranges to classify 200-299 as "success", 400-499 as "client error", 500-599 as "server error".
  • Variant groups: Create a KeyEvent enum with many variants and use or-patterns to group them into "printable", "control", and "navigation" in a single categorize function.
  • Open Source Repos