Advanced Patterns

Back

Loading concept...

🎯 Rust Pattern Matching: Advanced Patterns

The Story of the Super Detective 🕵️

Imagine you’re a super detective with a magical magnifying glass. This glass doesn’t just help you see things—it helps you find exactly what you’re looking for and grab just the pieces you need.

That’s what advanced pattern matching in Rust does! It’s like having superpowers to look at data and say:

  • “I only want numbers between 1 and 10” → Range patterns
  • “I want this, BUT only if something special is true” → Match guards
  • “Catch it AND give it a name!” → Binding with @
  • “Look at it without taking it!” → ref keyword
  • And so much more!

Let’s become pattern matching superheroes! 🦸


🌈 Range Patterns: “I Want Numbers Between Here and There!”

Think of a number line. Sometimes you don’t want ONE specific number—you want ALL numbers in a range!

What is a Range Pattern?

It’s like saying: “I’ll accept any number from 1 to 5.”

let age = 7;

match age {
    1..=5 => println!("Toddler!"),
    6..=12 => println!("Kid!"),
    13..=19 => println!("Teenager!"),
    _ => println!("Adult!"),
}
// Output: Kid!

🔑 Key Points

Syntax Meaning
1..=5 1 through 5 (includes 5)
1..5 1 through 4 (excludes 5)
// Works with characters too!
let letter = 'c';

match letter {
    'a'..='m' => println!("First half!"),
    'n'..='z' => println!("Second half!"),
    _ => println!("Something else"),
}
// Output: First half!

Remember: The ..= means “include the last one too!”


🛡️ Match Guards: “Yes, BUT Only If…”

A match guard is like adding an extra condition. It’s the detective saying: “I found a match, but let me double-check something first!”

What is a Match Guard?

It’s an if statement AFTER the pattern.

let number = 4;

match number {
    n if n % 2 == 0 => {
        println!("{} is even!", n)
    }
    n => println!("{} is odd!", n),
}
// Output: 4 is even!

Real-World Example

let temperature = 25;

match temperature {
    t if t < 0 => println!("Freezing! 🥶"),
    t if t < 15 => println!("Cold! 🧥"),
    t if t < 25 => println!("Nice! 😊"),
    t if t < 35 => println!("Hot! 🥵"),
    _ => println!("Too hot! 🔥"),
}
// Output: Hot! 🥵

The pattern matches first, THEN the guard checks!


🏷️ Binding with @ (At Symbol): “Catch AND Name It!”

Sometimes you want to match a pattern AND save the value with a name. The @ symbol is like putting a label on something you found!

What Does @ Do?

let age = 15;

match age {
    young @ 1..=12 => {
        println!("Young one: {} years", young)
    }
    teen @ 13..=19 => {
        println!("Teenager: {} years", teen)
    }
    adult => println!("Adult: {} years", adult),
}
// Output: Teenager: 15 years

The Magic Explained

teen @ 13..=19
 │      │
 │      └── Pattern to match (13 to 19)
 │
 └── Name we give to the matched value

Another Example

enum Message {
    Hello { id: i32 },
}

let msg = Message::Hello { id: 5 };

match msg {
    Message::Hello {
        id: special @ 3..=7,
    } => {
        println!("Special ID: {}", special)
    }
    Message::Hello { id } => {
        println!("Other ID: {}", id)
    }
}
// Output: Special ID: 5

👁️ The ref Keyword: “Look, Don’t Take!”

When matching, Rust normally takes ownership of what it matches. But sometimes you just want to look at something without taking it away!

The Problem

let name = String::from("Alice");

match name {
    n => println!("Hello, {}", n),
}
// After this, 'name' is MOVED into 'n'
// We can't use 'name' anymore!

The Solution: ref

let name = String::from("Alice");

match name {
    ref n => println!("Hello, {}", n),
}
// 'name' is still usable here!
println!("Name is: {}", name);

ref mut for Changing Values

let mut score = 10;

match score {
    ref mut s => *s += 5,
}

println!("Score: {}", score);
// Output: Score: 15

Think of ref as taking a photo instead of taking the actual thing! 📸


✅ Irrefutable Patterns: “This ALWAYS Works!”

An irrefutable pattern is one that always matches. It’s like saying “I accept anything!”

Examples of Irrefutable Patterns

// This ALWAYS works
let x = 5;          // x matches anything

// This ALWAYS works
let (a, b) = (1, 2); // Tuple destructure

// Function parameters are irrefutable
fn greet(name: String) {
    println!("Hello, {}", name);
}

Where Must You Use Them?

Place Requires
let statements Irrefutable
Function parameters Irrefutable
for loops Irrefutable
// This WON'T compile! ❌
// let Some(x) = some_option;

// This works! ✅
if let Some(x) = some_option {
    println!("{}", x);
}

❓ Refutable Patterns: “This MIGHT Not Match!”

A refutable pattern can fail to match. It’s like asking “Are you a dog?” to an unknown animal—it might be a cat!

Examples of Refutable Patterns

let maybe_number: Option<i32> = Some(42);

// Refutable: might be None!
match maybe_number {
    Some(n) => println!("Got: {}", n),
    None => println!("Nothing!"),
}

Where to Use Refutable Patterns

Place Refutable OK?
match arms ✅ Yes
if let ✅ Yes
while let ✅ Yes
let statement ❌ No
// Perfect for if let
if let Some(value) = maybe_number {
    println!("Value: {}", value);
}

// Perfect for while let
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
    println!("{}", top);
}

📋 Slice Patterns: “Look Inside Arrays!”

Slice patterns let you match parts of arrays or slices. It’s like looking at a train and saying “I want the first car, the last car, and everything in between!”

Basic Slice Matching

let numbers = [1, 2, 3, 4, 5];

match numbers {
    [first, .., last] => {
        println!("First: {}, Last: {}", first, last)
    }
}
// Output: First: 1, Last: 5

The .. (Rest Pattern)

The .. means “I don’t care about the middle stuff!”

let colors = ["red", "green", "blue", "yellow"];

match colors {
    [first, second, ..] => {
        println!("1st: {}, 2nd: {}", first, second)
    }
}
// Output: 1st: red, 2nd: green

Matching Specific Lengths

let data = [1, 2, 3];

match data {
    [] => println!("Empty!"),
    [x] => println!("One: {}", x),
    [x, y] => println!("Two: {}, {}", x, y),
    [x, y, z] => println!("Three: {}, {}, {}", x, y, z),
    _ => println!("Many!"),
}
// Output: Three: 1, 2, 3

Capturing the Rest

let numbers = [1, 2, 3, 4, 5];

match numbers {
    [first, rest @ ..] => {
        println!("First: {}", first);
        println!("Rest: {:?}", rest);
    }
}
// Output:
// First: 1
// Rest: [2, 3, 4, 5]

🔀 Or Patterns: “This OR That!”

Or patterns use | to match multiple possibilities. It’s like saying “I’ll take chocolate OR vanilla!”

Basic Or Pattern

let number = 2;

match number {
    1 | 2 | 3 => println!("One, two, or three!"),
    4 | 5 | 6 => println!("Four, five, or six!"),
    _ => println!("Something else!"),
}
// Output: One, two, or three!

Combining with Other Patterns

let point = (0, 5);

match point {
    (0, y) | (y, 0) => {
        println!("On an axis: {}", y)
    }
    (x, y) => println!("Point: ({}, {})", x, y),
}
// Output: On an axis: 5

With Enums

enum Color {
    Red,
    Green,
    Blue,
    Yellow,
}

let color = Color::Blue;

match color {
    Color::Red | Color::Yellow => {
        println!("Warm color!")
    }
    Color::Blue | Color::Green => {
        println!("Cool color!")
    }
}
// Output: Cool color!

🎮 Putting It All Together!

Let’s combine everything we learned:

fn analyze(data: &[i32]) {
    match data {
        // Empty slice
        [] => println!("No data!"),

        // Single element in range
        [x @ 1..=10] => {
            println!("Single small: {}", x)
        }

        // Two elements, first is special
        [first @ 1..=5, second]
            if *second > 10 => {
            println!(
                "Special pair: {} and {}",
                first, second
            )
        }

        // First, last, and rest
        [first, rest @ .., last]
            if first == last => {
            println!("Bookends: {}", first);
            println!("Middle: {:?}", rest);
        }

        // Catch all
        _ => println!("Other pattern"),
    }
}

fn main() {
    analyze(&[5, 20]);
    // Output: Special pair: 5 and 20

    analyze(&[3, 4, 5, 3]);
    // Output: Bookends: 3
    //         Middle: [4, 5]
}

🌟 Quick Reference

graph TD A["Advanced Patterns"] --> B["Range 1..=10"] A --> C["Guard if x &gt; 0"] A --> D["Binding n @ pattern"] A --> E["ref keyword"] A --> F["Irrefutable always match"] A --> G["Refutable might fail"] A --> H["Slice first, .., last"] A --> I["Or A &#124; B &#124; C"]

🎯 Remember These Superpowers!

Pattern Superpower Example
Range Match a range 1..=5
Guard Extra condition n if n > 0
@ Binding Name + Match x @ 1..=10
ref Borrow in match ref name
Irrefutable Always matches let x = 5
Refutable Might fail Some(x)
Slice Array parts [first, .., last]
Or Multiple options 1 | 2 | 3

You now have the superpowers of a Rust pattern matching hero! 🦸‍♀️🦸‍♂️

Go forth and match with confidence! 🚀

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.