.. and _ Wildcards
Tutorial Video
Text description (accessibility)
This video demonstrates the ".. and _ Wildcards" functional Rust example. Difficulty level: Fundamental. Key concepts covered: Functional Programming. Large structs and long tuples often have many fields, but a given function cares about only one or two. Key difference from OCaml: 1. **`..` vs `_`**: Rust's `..` in struct patterns ignores all unlisted fields; OCaml uses `_` for each field individually or relies on a compiler flag to suppress partial
Tutorial
The Problem
Large structs and long tuples often have many fields, but a given function cares about only one or two. Without wildcards, every pattern must mention every field. _ ignores a single element; .. ignores zero or more elements in struct patterns and slice patterns. Together they enable precise, readable patterns that name exactly the data you need. This is particularly important for forward compatibility — using .. in struct patterns means adding new fields to the struct does not break existing match arms.
🎯 Learning Outcomes
_ ignores a single field or variable binding.. ignores remaining struct fields or middle slice elements(a, _, _, _) extracts the first element of a 4-tuplePoint { x, .. } extracts just x from a struct with many fields.. in struct patterns is important for API evolution and forward compatibilityCode Example
#![allow(clippy::all)]
//! .. and _ Wildcards
//!
//! Ignoring parts of a pattern.
pub struct Point {
pub x: i32,
pub y: i32,
pub z: i32,
}
/// Ignore with _.
pub fn get_x(p: &Point) -> i32 {
match p {
Point { x, y: _, z: _ } => *x,
}
}
/// Ignore multiple with ...
pub fn get_x_short(p: &Point) -> i32 {
let Point { x, .. } = p;
*x
}
/// Ignore in tuple.
pub fn first_of_four((a, _, _, _): (i32, i32, i32, i32)) -> i32 {
a
}
/// Ignore middle elements.
pub fn ends((first, .., last): (i32, i32, i32, i32, i32)) -> (i32, i32) {
(first, last)
}
/// Wildcard in match.
pub fn is_special(n: i32) -> bool {
match n {
0 | 42 | 100 => true,
_ => false,
}
}
/// Ignore enum data.
#[derive(Debug)]
pub enum Event {
Click(i32, i32),
Scroll(f32),
KeyPress(char),
}
pub fn is_click(e: &Event) -> bool {
matches!(e, Event::Click(..))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_x() {
let p = Point { x: 1, y: 2, z: 3 };
assert_eq!(get_x(&p), 1);
assert_eq!(get_x_short(&p), 1);
}
#[test]
fn test_first_of_four() {
assert_eq!(first_of_four((1, 2, 3, 4)), 1);
}
#[test]
fn test_ends() {
assert_eq!(ends((1, 2, 3, 4, 5)), (1, 5));
}
#[test]
fn test_is_special() {
assert!(is_special(42));
assert!(!is_special(7));
}
#[test]
fn test_is_click() {
assert!(is_click(&Event::Click(0, 0)));
assert!(!is_click(&Event::KeyPress('a')));
}
}Key Differences
.. vs _**: Rust's .. in struct patterns ignores all unlisted fields; OCaml uses _ for each field individually or relies on a compiler flag to suppress partial-pattern warnings... cause compile errors when new fields are added to the struct; OCaml partial record patterns cause warnings...**: Rust tuple struct .. ignores trailing fields; OCaml requires explicit _ for each ignored position.[first, .., last] skips middle elements; OCaml arrays require explicit indexing for this pattern.OCaml Approach
OCaml's _ and _field work identically, and record patterns also support partial matching:
let get_x { x; _ } = x (* _ ignores other fields *)
let first_of_four (a, _, _, _) = a
OCaml requires listing all non-ignored record fields explicitly by default (compiler warning for partial patterns).
Full Source
#![allow(clippy::all)]
//! .. and _ Wildcards
//!
//! Ignoring parts of a pattern.
pub struct Point {
pub x: i32,
pub y: i32,
pub z: i32,
}
/// Ignore with _.
pub fn get_x(p: &Point) -> i32 {
match p {
Point { x, y: _, z: _ } => *x,
}
}
/// Ignore multiple with ...
pub fn get_x_short(p: &Point) -> i32 {
let Point { x, .. } = p;
*x
}
/// Ignore in tuple.
pub fn first_of_four((a, _, _, _): (i32, i32, i32, i32)) -> i32 {
a
}
/// Ignore middle elements.
pub fn ends((first, .., last): (i32, i32, i32, i32, i32)) -> (i32, i32) {
(first, last)
}
/// Wildcard in match.
pub fn is_special(n: i32) -> bool {
match n {
0 | 42 | 100 => true,
_ => false,
}
}
/// Ignore enum data.
#[derive(Debug)]
pub enum Event {
Click(i32, i32),
Scroll(f32),
KeyPress(char),
}
pub fn is_click(e: &Event) -> bool {
matches!(e, Event::Click(..))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_x() {
let p = Point { x: 1, y: 2, z: 3 };
assert_eq!(get_x(&p), 1);
assert_eq!(get_x_short(&p), 1);
}
#[test]
fn test_first_of_four() {
assert_eq!(first_of_four((1, 2, 3, 4)), 1);
}
#[test]
fn test_ends() {
assert_eq!(ends((1, 2, 3, 4, 5)), (1, 5));
}
#[test]
fn test_is_special() {
assert!(is_special(42));
assert!(!is_special(7));
}
#[test]
fn test_is_click() {
assert!(is_click(&Event::Click(0, 0)));
assert!(!is_click(&Event::KeyPress('a')));
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_x() {
let p = Point { x: 1, y: 2, z: 3 };
assert_eq!(get_x(&p), 1);
assert_eq!(get_x_short(&p), 1);
}
#[test]
fn test_first_of_four() {
assert_eq!(first_of_four((1, 2, 3, 4)), 1);
}
#[test]
fn test_ends() {
assert_eq!(ends((1, 2, 3, 4, 5)), (1, 5));
}
#[test]
fn test_is_special() {
assert!(is_special(42));
assert!(!is_special(7));
}
#[test]
fn test_is_click() {
assert!(is_click(&Event::Click(0, 0)));
assert!(!is_click(&Event::KeyPress('a')));
}
}
Deep Comparison
OCaml vs Rust: pattern dotdot wildcard
See example.rs and example.ml for implementations.
Exercises
w: i32 field to Point3D { x, y, z } and verify that patterns using .. continue compiling without modification, while patterns without .. report an error.fn extract_leaf(tree: &Tree) -> Option<i32> where Tree is a nested struct with many fields — use .. to ignore fields at each level and extract only the leaf value.fn third_of_five<T: Copy>((_, _, x, _, _): (T, T, T, T, T)) -> T using _ for each ignored position — compare readability with an equivalent index-based version.