Destructuring → To extract values from complex data structures in a clean and concise way.
Guards→ To apply additional conditions while matching so only valid cases are accepted.
Binding→ To capture matched values into variables for immediate and later use.
Short Purpose-Focused Explanation
1️⃣
Destructuring – Why we use it
Makes code shorter and cleaner
Improves readability
Extracts multiple values at once
Reduces manual indexing or field access
Without destructuring: verbose, error-prone
With destructuring: clean and expressive
2️⃣
Guards – Why we use them
Add business rules during matching
Avoid deep if-else nesting
Improve decision clarity
Ensure only valid data flows forward
Guards = “Match this pattern only if condition is true”
3️⃣
Binding – Why we use it
Captures values during matching
Avoids duplicate variable declarations
Makes logic single-pass
Improves performance and clarity
Binding = “Match + store value in one step”
Final Summary (Perfect for notes / W4)
Destructuring → Extract
Guards → Validate
Binding → Store
Destructuring (Pattern Matching)
Theory (simple)
Destructuring means breaking a compound value (tuple/array/struct/enum) into smaller variables using patterns like:
let (a,b) = tuple;
match value { pattern => ... }
1) Destructuring a tuple
fn main() {
let t = (10, "Hi", true);
let (a, b, c) = t;
println!("{}, {}, {}", a, b, c);
}
Output
10, Hi, true
2) Destructuring a struct
#[derive(Debug)]
struct User {
name: String,
age: u8,
}
fn main() {
let u = User { name: "Ashwani".to_string(), age: 27 };
let User { name, age } = u; // destructure
println!("name={}, age={}", name, age);
}
Output
name=Ashwani, age=27
Destructuring a tuple struct
#[derive(Debug)]
struct Point(i32, i32);
fn main() {
let p = Point(5, 7);
let Point(x, y) = p;
println!("x={}, y={}", x, y);
}
Output
x=5, y=7
Destructuring an array + slice pattern
fn main() {
let arr = [1, 2, 3, 4, 5];
match arr {
[first, second, .., last] => println!("first={}, second={}, last={}", first, second, last),
}
}
Output
first=1, second=2, last=5
Destructuring an enum
enum Payment {
Cash(i32),
Upi { txn_id: String, amount: i32 },
}
fn main() {
let p = Payment::Upi { txn_id: "TXN99".to_string(), amount: 500 };
match p {
Payment::Cash(a) => println!("Cash: {}", a),
Payment::Upi { txn_id, amount } => println!("UPI {} amount {}", txn_id, amount),
}
}
Output
UPI TXN99 amount 500
Guards (match with conditions)
Theory (simple)
A guard is an extra if condition inside a match arm:
match x {
pattern if condition => ...
_ => ...
}
It helps you match only when the condition is true.
1) Guard with integer range
fn main() {
let x = 15;
match x {
n if n < 0 => println!("Negative"),
n if n <= 10 => println!("0 to 10"),
_ => println!("Above 10"),
}
}
Output
Above 10
2) Guard with tuple
fn main() {
let p = (0, 7);
match p {
(0, y) if y > 0 => println!("On Y-axis above origin: y={}", y),
(0, y) => println!("On Y-axis y={}", y),
_ => println!("Not on Y-axis"),
}
}
Output
On Y-axis above origin: y=7
3) Guard with enum variant
enum Status {
Ok(i32),
Err(String),
}
fn main() {
let s = Status::Ok(120);
match s {
Status::Ok(v) if v >= 100 => println!("High OK: {}", v),
Status::Ok(v) => println!("Normal OK: {}", v),
Status::Err(e) => println!("Error: {}", e),
}
}
Output
High OK: 120
4) Guard to filter even/odd
fn main() {
let n = 8;
match n {
x if x % 2 == 0 => println!("Even"),
_ => println!("Odd"),
}
}
Output
Even
5) Guard with string length
fn main() {
let name = "Ashwani";
match name {
s if s.len() > 5 => println!("Long name"),
_ => println!("Short name"),
}
}
Output
Long name
Binding (capture values using @)
Theory (simple)
Binding using @ lets you:
capture a matched value
while still checking it matches a pattern/range
Example:
n @ 1..=5
Means:
match range 1 to 5
also store that value in n
1) Bind number within range
fn main() {
let x = 4;
match x {
n @ 1..=5 => println!("In range: {}", n),
_ => println!("Out of range"),
}
}
Output
In range: 4
2) Bind and match enum
enum Login {
Success(String),
Failed,
}
fn main() {
let l = Login::Success("Ashwani".to_string());
match l {
s @ Login::Success(_) => println!("Matched success: {:?}", "Success"),
Login::Failed => println!("Failed"),
}
}
Output
Matched success: "Success"
3) Bind with struct field pattern
#[derive(Debug)]
struct Emp {
id: i32,
salary: i32,
}
fn main() {
let e = Emp { id: 101, salary: 90000 };
match e {
emp @ Emp { salary: 80000..=100000, .. } => println!("Mid-high employee: {:?}", emp.id),
_ => println!("Other employee"),
}
}
Output
Mid-high employee: 101
4) Bind with tuple patterns
fn main() {
let t = (3, 9);
match t {
pair @ (x, y) if x < y => println!("Increasing pair {:?} ({} < {})", pair, x, y),
_ => println!("Not increasing"),
}
}
Output
Increasing pair (3, 9) (3 < 9)
5) Bind with slice pattern
fn main() {
let arr = [10, 20, 30, 40];
match arr {
whole @ [10, ..] => println!("Starts with 10: {:?}", whole),
_ => println!("Different start"),
}
}
Output
Starts with 10: [10, 20, 30, 40]
Top comments (0)