Nested Patterns
Tutorial Video
Text description (accessibility)
This video demonstrates the "Nested Patterns" functional Rust example. Difficulty level: Fundamental. Key concepts covered: Functional Programming. Real data structures are rarely flat — they are trees: enums containing structs containing options containing results. Key difference from OCaml: 1. **Struct syntax**: Rust `Outer { inner: Inner { value } }` uses braces; OCaml `{ inner = { value } }` uses `=` for record fields.
Tutorial
The Problem
Real data structures are rarely flat — they are trees: enums containing structs containing options containing results. Nested patterns allow matching deeply nested structures in a single expression, extracting values from multiple levels simultaneously. Without nested patterns, extracting a value from Some(Some(Point { x, y })) would require multiple nested match statements. Nested patterns are essential in AST traversal, JSON processing, configuration parsing, and any domain with layered data.
🎯 Learning Outcomes
Outer { inner: Inner { value } } matches and extracts from nested structsSome(Some(v)) matches and extracts from nested OptionOk(Ok(v)) and Ok(Err(msg)) handle nested ResultCode Example
#![allow(clippy::all)]
//! Nested Patterns
//!
//! Matching deeply nested structures.
#[derive(Debug)]
pub struct Outer {
pub inner: Inner,
}
#[derive(Debug)]
pub struct Inner {
pub value: i32,
}
/// Match nested struct.
pub fn get_value(o: &Outer) -> i32 {
match o {
Outer {
inner: Inner { value },
} => *value,
}
}
/// Nested Option.
pub fn unwrap_nested(opt: Option<Option<i32>>) -> i32 {
match opt {
Some(Some(v)) => v,
Some(None) => -1,
None => -2,
}
}
/// Nested Result.
pub fn process_nested(res: Result<Result<i32, &str>, &str>) -> i32 {
match res {
Ok(Ok(v)) => v,
Ok(Err(_)) => -1,
Err(_) => -2,
}
}
/// Deeply nested.
pub fn deep_match(data: Option<(i32, Option<(i32, i32)>)>) -> i32 {
match data {
Some((a, Some((b, c)))) => a + b + c,
Some((a, None)) => a,
None => 0,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_value() {
let o = Outer {
inner: Inner { value: 42 },
};
assert_eq!(get_value(&o), 42);
}
#[test]
fn test_unwrap_nested() {
assert_eq!(unwrap_nested(Some(Some(5))), 5);
assert_eq!(unwrap_nested(Some(None)), -1);
assert_eq!(unwrap_nested(None), -2);
}
#[test]
fn test_process_nested() {
assert_eq!(process_nested(Ok(Ok(10))), 10);
assert_eq!(process_nested(Ok(Err("e"))), -1);
assert_eq!(process_nested(Err("e")), -2);
}
#[test]
fn test_deep_match() {
assert_eq!(deep_match(Some((1, Some((2, 3))))), 6);
assert_eq!(deep_match(Some((5, None))), 5);
assert_eq!(deep_match(None), 0);
}
}Key Differences
Outer { inner: Inner { value } } uses braces; OCaml { inner = { value } } uses = for record fields.? operator and and_then chains are often cleaner for nested Option/Result.match but without the code duplication.OCaml Approach
OCaml nested patterns are identical in expressive power:
let get_value { inner = { value } } = value
let unwrap_nested = function
| Some (Some v) -> v
| Some None -> -1
| None -> -2
The syntax differs slightly but the capability is the same.
Full Source
#![allow(clippy::all)]
//! Nested Patterns
//!
//! Matching deeply nested structures.
#[derive(Debug)]
pub struct Outer {
pub inner: Inner,
}
#[derive(Debug)]
pub struct Inner {
pub value: i32,
}
/// Match nested struct.
pub fn get_value(o: &Outer) -> i32 {
match o {
Outer {
inner: Inner { value },
} => *value,
}
}
/// Nested Option.
pub fn unwrap_nested(opt: Option<Option<i32>>) -> i32 {
match opt {
Some(Some(v)) => v,
Some(None) => -1,
None => -2,
}
}
/// Nested Result.
pub fn process_nested(res: Result<Result<i32, &str>, &str>) -> i32 {
match res {
Ok(Ok(v)) => v,
Ok(Err(_)) => -1,
Err(_) => -2,
}
}
/// Deeply nested.
pub fn deep_match(data: Option<(i32, Option<(i32, i32)>)>) -> i32 {
match data {
Some((a, Some((b, c)))) => a + b + c,
Some((a, None)) => a,
None => 0,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_value() {
let o = Outer {
inner: Inner { value: 42 },
};
assert_eq!(get_value(&o), 42);
}
#[test]
fn test_unwrap_nested() {
assert_eq!(unwrap_nested(Some(Some(5))), 5);
assert_eq!(unwrap_nested(Some(None)), -1);
assert_eq!(unwrap_nested(None), -2);
}
#[test]
fn test_process_nested() {
assert_eq!(process_nested(Ok(Ok(10))), 10);
assert_eq!(process_nested(Ok(Err("e"))), -1);
assert_eq!(process_nested(Err("e")), -2);
}
#[test]
fn test_deep_match() {
assert_eq!(deep_match(Some((1, Some((2, 3))))), 6);
assert_eq!(deep_match(Some((5, None))), 5);
assert_eq!(deep_match(None), 0);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_value() {
let o = Outer {
inner: Inner { value: 42 },
};
assert_eq!(get_value(&o), 42);
}
#[test]
fn test_unwrap_nested() {
assert_eq!(unwrap_nested(Some(Some(5))), 5);
assert_eq!(unwrap_nested(Some(None)), -1);
assert_eq!(unwrap_nested(None), -2);
}
#[test]
fn test_process_nested() {
assert_eq!(process_nested(Ok(Ok(10))), 10);
assert_eq!(process_nested(Ok(Err("e"))), -1);
assert_eq!(process_nested(Err("e")), -2);
}
#[test]
fn test_deep_match() {
assert_eq!(deep_match(Some((1, Some((2, 3))))), 6);
assert_eq!(deep_match(Some((5, None))), 5);
assert_eq!(deep_match(None), 0);
}
}
Deep Comparison
OCaml vs Rust: pattern nested
See example.rs and example.ml for implementations.
Exercises
struct A { b: Option<B> }; struct B { c: Vec<C> }; struct C { value: i32 } and write a function that extracts the first C.value if all levels are present.Config { database: Option<DbConfig { host: String, port: u16 }> } and write fn db_host(c: &Config) -> Option<&str> using a single nested pattern.Expr { Add(Box<Expr>, Box<Expr>), Lit(i32) } and write an eval function using nested match patterns.