পাঠ ১৯.৩

Pattern Syntax

Pattern Syntax

এই পাঠে Rust-এ pattern লেখার সব valid syntax — literal match, variable, multiple pattern, range, destructuring, ignoring, match guard, এবং @ binding।

Literal match

fn main() {
    let x = 1;

    match x {
        1 => println!("one"),
        2 => println!("two"),
        3 => println!("three"),
        _ => println!("anything"),
    }
}

x 1 — তাই "one" print। নির্দিষ্ট value-এর জন্য particular action — straightforward।

Named variable — shadowing

Named variable irrefutable pattern — যেকোনো value match করে। কিন্তু match, if let, while let-এর pattern-এ declare হলে — outer scope-এর same name-এর variable-কে shadow করে।

src/main.rsrust
fn main() {
    let x = Some(5);
    let y = 10;

    match x {
        Some(50) => println!("Got 50"),
        Some(y) => println!("Matched, y = {y}"),
        _ => println!("Default case, x = {x:?}"),
    }

    println!("at the end: x = {x:?}, y = {y}");
}

Second arm-এর Some(y)-এর y নতুন — match-এর scope-এ। বাইরের y = 10-কে shadow করছে। x = Some(5) → second arm match → inner y = 5 → print "Matched, y = 5"।

Match শেষ — inner y-ও শেষ। শেষ println-এ x = Some(5), y = 10 (outer)।

বাইরের y-এর সাথে compare করতে চাইলে — নিচে match guard

Multiple pattern — | (OR)

fn main() {
    let x = 1;

    match x {
        1 | 2 => println!("one or two"),
        3 => println!("three"),
        _ => println!("anything"),
    }
}

1 | 2 — যেকোনোটা match হলেই arm execute। Print হবে "one or two"।

Range — ..=

fn main() {
    let x = 5;

    match x {
        1..=5 => println!("one through five"),
        _ => println!("something else"),
    }
}

1..=5 — inclusive range, 1 থেকে 5 পর্যন্ত। 1 | 2 | 3 | 4 | 5-এর চেয়ে অনেক ছোট।

Range শুধু numeric এবং char-এ allowed — কারণ compiler compile time-এ check করে range empty কিনা।

fn main() {
    let x = 'c';

    match x {
        'a'..='j' => println!("early ASCII letter"),
        'k'..='z' => println!("late ASCII letter"),
        _ => println!("something else"),
    }
}

'c' first range-এ — print "early ASCII letter"।

Destructuring — struct

src/main.rsrust
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 0, y: 7 };

    let Point { x: a, y: b } = p;
    assert_eq!(0, a);
    assert_eq!(7, b);
}

Point destructure করে field-এর value-গুলো আলাদা variable-এ — x → a, y → b। Variable name field name-এর সাথে মেলাতে হয় না, কিন্তু সাধারণত মেলানোই হয়। Shorthand:

src/main.rsrust
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 0, y: 7 };

    let Point { x, y } = p;
    assert_eq!(0, x);
    assert_eq!(7, y);
}

Field name-ই variable name।

Literal-এর সাথে mix করেও test করা যায়:

src/main.rsrust
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 0, y: 7 };

    match p {
        Point { x, y: 0 } => println!("On the x axis at {x}"),
        Point { x: 0, y } => println!("On the y axis at {y}"),
        Point { x, y } => {
            println!("On neither axis: ({x}, {y})");
        }
    }
}

First arm — y == 0, x bound। Second arm — x == 0, y bound। p ={ x: 0, y: 7 } → second arm match → "On the y axis at 7"।

Destructuring — enum

src/main.rsrust
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

fn main() {
    let msg = Message::ChangeColor(0, 160, 255);

    match msg {
        Message::Quit => {
            println!("The Quit variant has no data to destructure.");
        }
        Message::Move { x, y } => {
            println!("Move in the x direction {x} and in the y direction {y}");
        }
        Message::Write(text) => {
            println!("Text message: {text}");
        }
        Message::ChangeColor(r, g, b) => {
            println!("Change color to red {r}, green {g}, and blue {b}");
        }
    }
}

Enum variant-এর data যেভাবে define, pattern-ও সেভাবে। Quit-এ data নেই — শুধু literal match। Move-এ struct-style — curly braces। Write tuple-style — parentheses। ChangeColor — তিনটা i32। Number/order মিলতে হবে।

Nested struct/enum

enum Color {
    Rgb(i32, i32, i32),
    Hsv(i32, i32, i32),
}

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(Color),
}

fn main() {
    let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));

    match msg {
        Message::ChangeColor(Color::Rgb(r, g, b)) => {
            println!("Change color to red {r}, green {g}, and blue {b}");
        }
        Message::ChangeColor(Color::Hsv(h, s, v)) => {
            println!("Change color to hue {h}, saturation {s}, value {v}");
        }
        _ => (),
    }
}

Pattern arbitrary depth-এ nest করতে পারে।

Struct + tuple একসাথে

fn main() {
    struct Point {
        x: i32,
        y: i32,
    }

    let ((feet, inches), Point { x, y }) = ((3, 10), Point { x: 3, y: -10 });
}

জটিল type-কে component-এ ভাঙা — সব একসাথে।

Value ignore — _ এবং ..

Entire value — _

src/main.rsrust
fn foo(_: i32, y: i32) {
    println!("This code only uses the y parameter: {y}");
}

fn main() {
    foo(3, 4);
}

First parameter ignore — print "This code only uses the y parameter: 4"।

Nested _ — পার্ট-wise ignore

fn main() {
    let mut setting_value = Some(5);
    let new_setting_value = Some(10);

    match (setting_value, new_setting_value) {
        (Some(_), Some(_)) => {
            println!("Can't overwrite an existing customized value");
        }
        _ => {
            setting_value = new_setting_value;
        }
    }

    println!("setting is {setting_value:?}");
}

দু'জনেই Some — value নেওয়ার দরকার নেই, শুধু variant-টা match হলেই হলো। Print "Can't overwrite an existing customized value", তারপর "setting is Some(5)"।

একাধিক জায়গায় _:

fn main() {
    let numbers = (2, 4, 8, 16, 32);

    match numbers {
        (first, _, third, _, fifth) => {
            println!("Some numbers: {first}, {third}, {fifth}");
        }
    }
}

Print "Some numbers: 2, 8, 32"।

_-এর শুরুর variable name

Variable বানিয়ে use না করলে Rust warning দেয়। Name-এর শুরুতে _ দিলে warning suppress।

src/main.rsrust
fn main() {
    let _x = 5;
    let y = 10;
}

_x-এ warning নেই, y unused — warning।

সূক্ষ্ম তফাৎ — শুধু _ bind করে না, কিন্তু _x bind করে। নিচের code error:

fn main() {
    let s = Some(String::from("Hello!"));

    if let Some(_s) = s {
        println!("found a string");
    }

    println!("{s:?}");
}

s move হয়ে গেছে _s variable-এ। শেষ println-এ s use — error।

শুধু _ দিলে কোনো bind হয় না:

fn main() {
    let s = Some(String::from("Hello!"));

    if let Some(_) = s {
        println!("found a string");
    }

    println!("{s:?}");
}

এখানে s move হয়নি — কোনো issue নেই।

.. — remaining ignore

fn main() {
    struct Point {
        x: i32,
        y: i32,
        z: i32,
    }

    let origin = Point { x: 0, y: 0, z: 0 };

    match origin {
        Point { x, .. } => println!("x is {x}"),
    }
}

শুধু x দরকার, বাকি সব ..

Tuple-এ first-last:

src/main.rsrust
fn main() {
    let numbers = (2, 4, 8, 16, 32);

    match numbers {
        (first, .., last) => {
            println!("Some numbers: {first}, {last}");
        }
    }
}

Print "Some numbers: 2, 32"। কিন্তু .. ambiguous হলে error:

src/main.rsrust
fn main() {
    let numbers = (2, 4, 8, 16, 32);

    match numbers {
        (.., second, ..) => {
            println!("Some numbers: {second}")
        },
    }
}
compile errortext
error: `..` can only be used once per tuple pattern
 --> src/main.rs:5:22
  |
5 |         (.., second, ..) => {
  |          --          ^^ can only be used once per tuple pattern
  |          |
  |          previously used here

second-এর আগে কয়টা element ignore আর পরে কয়টা — Rust বলতে পারছে না।

Match guard — অতিরিক্ত if

Match arm-এর pattern-এর পরে if condition — সেটাও true হলে arm execute। শুধু match-এ available, if let / while let-এ না।

fn main() {
    let num = Some(4);

    match num {
        Some(x) if x % 2 == 0 => println!("The number {x} is even"),
        Some(x) => println!("The number {x} is odd"),
        None => (),
    }
}

Some(4) match → guard 4 % 2 == 0 → true → "The number 4 is even"। Some(5) হলে guard fail, second arm-এ যেত।

আগের shadowing সমস্যা — outer y-এর সাথে compare:

src/main.rsrust
fn main() {
    let x = Some(5);
    let y = 10;

    match x {
        Some(50) => println!("Got 50"),
        Some(n) if n == y => println!("Matched, n = {n}"),
        _ => println!("Default case, x = {x:?}"),
    }

    println!("at the end: x = {x:?}, y = {y}");
}

Some(n)n outer কোনো variable-কে shadow করে না। Guard-এ n == y — সেখানে y outer। n = 5, y = 10 — guard fail; default arm match — "Default case, x = Some(5)"।

Match guard |-এর সাথেও:

fn main() {
    let x = 4;
    let y = false;

    match x {
        4 | 5 | 6 if y => println!("yes"),
        _ => println!("no"),
    }
}

Guard পুরো 4 | 5 | 6-এ apply — (4 | 5 | 6) if y। x = 4 match, কিন্তু y false — fail। Print "no"।

@ binding — test ও bind একসাথে

@ দিয়ে value-কে test করতে করতেই variable-এ bind করা যায়।

fn main() {
    enum Message {
        Hello { id: i32 },
    }

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

    match msg {
        Message::Hello { id: id @ 3..=7 } => {
            println!("Found an id in range: {id}")
        }
        Message::Hello { id: 10..=12 } => {
            println!("Found an id in another range")
        }
        Message::Hello { id } => println!("Found some other id: {id}"),
    }
}

id @ 3..=7 — id 3 থেকে 7-এর মধ্যে কিনা test, মিললে value id variable-এ bind। Print "Found an id in range: 5"।

Second arm-এ শুধু range — value variable-এ নেই। Third arm-এ শুধু id — যেকোনো value, কোনো test নেই।

এই পাঠ থেকে যা শিখলে

  • Literal, named variable (shadowing), multiple |, range ..= — basic pattern।
  • Struct, enum, tuple destructure — যেকোনো depth-এ nest; shorthand-ও available।
  • Ignore — _ (entire/part), _var (binds, no warning), .. (remaining)।
  • Match guard if — outer variable compare, একই pattern-এ extra condition।
  • @ — test এবং bind একসাথে।