Structs, Enums and Pattern Matching in Rust

A
Author
10 Aug, 2026 1 min read 754 views

Rust's enums are far more powerful than enums in most languages: each variant can hold its own data. Combined with match, they let you model states so that invalid ones can't exist.

Structs and impl

#[derive(Debug, Clone)]
struct User {
    id: u64,
    name: String,
    email: Option<String>,
}

impl User {
    fn new(id: u64, name: &str) -> Self {
        Self { id, name: name.to_string(), email: None }
    }

    fn display_name(&self) -> &str {
        &self.name
    }
}

Enums with data

enum PaymentStatus {
    Pending,
    Paid { transaction_id: String, amount: u64 },
    Failed(String),     // reason
    Refunded { amount: u64 },
}

match must cover every case

fn describe(status: &PaymentStatus) -> String {
    match status {
        PaymentStatus::Pending => "Waiting for payment".to_string(),
        PaymentStatus::Paid { amount, .. } => format!("Paid ₹{}", amount / 100),
        PaymentStatus::Failed(reason) => format!("Failed: {reason}"),
        PaymentStatus::Refunded { amount } if *amount == 0 => "Nothing refunded".into(),
        PaymentStatus::Refunded { amount } => format!("Refunded ₹{}", amount / 100),
    }
}

If you add a new variant later, every match that doesn't handle it stops compiling. That's how Rust keeps big codebases consistent.

if let and let else

// When you only care about one case
if let Some(email) = &user.email {
    send_welcome(email);
}

// Unwrap or return early
fn domain(user: &User) -> Option<&str> {
    let Some(email) = &user.email else {
        return None;
    };
    email.split('@').nth(1)
}

Patterns everywhere

let (x, y) = (3, 4);                       // destructure a tuple
let User { name, .. } = user.clone();      // destructure a struct
match code { 200..=299 => "ok", 404 => "missing", _ => "error" };
Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.