Skip to content

Latest commit

 

History

History
239 lines (195 loc) · 4.58 KB

20_patterns.md

File metadata and controls

239 lines (195 loc) · 4.58 KB

Patterns

Patterns can be matched and destructured in various ways:

fn main() {
    let t = (1, 3, 37);
    let (x, y, z) = t;
    let r = match t {
        (420, ..) => "blazeit",
        (0, 0, 0) => "unit",
        _ => "other stuff",
    };
}

Destructuring

Values can be destructured in match-like expressions and whenever they're being assigned to a name, like variable declaration or function arguments:

fn double_vec((x, y): (i32, i32)) -> (i32, i32) {
    (x * 2, y * 2)
}

fn main() {
    let v = (4, 2, 0);
    let (x, y, z) = v;
}

Literals can be matched directly or as a range, or bind to a name:

fn main() {
    let n = 5;
    match n {
        1337 => println!("leet"),
        0..=10 => prinln!("zero to ten"),
        x => println!("got a number {}", x),
    };
}

Bound literals can shadow names:

fn main() {
    let n = Some(5);
    if let Some(n) = n {
        println!("got Some({})", n);
    };
}

A catch-all placeholder can be specified using _:

fn main() {
    let n = 5;
    match n {
        1..=10 => prinln!("one to ten"),
        _ => println!("some irrelevant number"),
    };
}

Destructuring is recursive:

fn main() {
    let x = Some(((13, 37), 15, "omg"));
    if let Some(((x, y), ..)) = x {
        println!("x = {}, y = {}", x, y);
    };
}

Tuples

Tuples can be destructured into individual components. The lead or tail part of a tuple can be ignored using the .. syntax:

fn main() {
    let v = (1, 3, 7);
    match v {
        (4, 2, 0) => println!("blaze it"),
        (x, y, 0) => println!("2D vector {:?}", (x, y)),
        (1, ..) => println!("1 is first"),
        (.., 1) => println!("1 is last"),
        (x, y, z) => println!("x = {}, y = {}, z = {}", x, y, z),
    };
}

Structs

Structs can be destructured to individual fields. Fields can be ignored using the .. syntax. A shorthand for field destructuring can be used when binding to the same name as the field name:

struct Screen {
    width: i32,
    height: i32,
}

fn main() {
    let s = Screen {
        width: 1337,
        height: 420,
    };

    match s {
        Screen {
            width: 1920,
            height: 1080,
        } => println!("1080p"),
        Screen { width: 0..=480, .. } => println!("what a narrow screen"),
        Screen { width: 1000, height } => println!("width 1000 with height {}", height),
        Screen { width: w, height: h } => println!("{}x{}", w, h),
    };
}

Pointers

References can be destructured into values:

fn main() {
    let n = &5;
    match n {
        &val => println!("n by value: {}", val),
    }
}

Values can be made into references using ref or ref mut:

fn main() {
    let s = "Hello".to_string();
    match s {
        ref s => println!("not moved! just ref'd: {}", s),
    }

    let mut s = s;
    match s {
        ref mut s => {
            s.push_str(", world!");
        }
    }
    println!("{}", s);
}

Enums

Individual enum variants can be matched and further destructured:

enum Attribute {
    Empty,
    Color(u8, u8, u8),
    Text(String),
    Place { x: i32, y: i32 },
}

fn main() {
    let a = Attribute::Empty;
    match a {
        Attribute::Empty => println!("empty 🤷"),
        Attribute::Color(255, ..) => println!("something with red"),
        Attribute::Text(ref s) => println!("got text \"{}\"", s),
        Attribute::Place { x: 0, y: 0 } => println!("origin"),
        _ => println!("something different"),
    };
}

Guards

Match guards are created using the if keyword and a boolean expression:

fn main() {
    let pair = (2, -2);
    match pair {
        (x, y) if x == y => println!("same numbers"),
        (x, y) if x + y == 0 => println!("opposites"),
        (x, _) if x % 2 == 1 => println!("odd first"),
        _ => println!("something different"),
    }
}

Or

Multiple patterns can be matched using |:

fn main() {
    let s = "lol";
    let cnd = false;
    match s {
        "lol" | "kek" | "bur" => println!("lmao"),
        "omg" | "wtf" if cnd => println!("conditional or"),
        _ => println!("shrug"),
    };
}

Binding

Matched literals can be bound to a name using the @ symbol:

fn main() {
    let n = 5;
    match n {
        x @ 0 => println!("zero"),
        x @ 1..=10 => println!("one to ten, got {}" x),
        _ => println!("something different"),
    };
}

Unused names

Bound names starting with _ are not checked as unused by the compiler:

fn main() {
    let _x = 5; // whatever
}