ExamplesBy LevelBy TopicLearning Paths
571 Fundamental

.. and _ Wildcards

Functional Programming

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

  • • How _ ignores a single field or variable binding
  • • How .. ignores remaining struct fields or middle slice elements
  • • How (a, _, _, _) extracts the first element of a 4-tuple
  • • How Point { x, .. } extracts just x from a struct with many fields
  • • Why .. in struct patterns is important for API evolution and forward compatibility
  • Code 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.
  • Forward compat: Rust struct patterns without .. cause compile errors when new fields are added to the struct; OCaml partial record patterns cause warnings.
  • **Tuple ..**: Rust tuple struct .. ignores trailing fields; OCaml requires explicit _ for each ignored position.
  • Slice middle: Rust [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')));
        }
    }
    ✓ Tests Rust test suite
    #[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

  • Add field safely: Add a w: i32 field to Point3D { x, y, z } and verify that patterns using .. continue compiling without modification, while patterns without .. report an error.
  • Deep ignore: Write 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.
  • Tuple third: Write 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.
  • Open Source Repos