Or Patterns
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
| in a single match arm matches multiple alternativesmatches!(value, A | B | C) provides a concise boolean checkif let and let destructuringCode 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
matches! macro**: Rust's matches! is a convenient shorthand; OCaml achieves the same with a function | Pat1 | Pat2 -> true | _ -> false.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));
}
}#[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
fn is_arithmetic_op(c: char) -> bool using or-patterns to check for +, -, *, /, %.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".KeyEvent enum with many variants and use or-patterns to group them into "printable", "control", and "navigation" in a single categorize function.