🎯

Enums & Pattern Matching — A Powerful Evolution of Symbol and case/when

Rust enums can carry data — a concept Ruby doesn't have

In Ruby, you use Symbols for states. :active, :inactive, :pending. Rust's enum fills this role.

enum Status {
    Active,
    Inactive,
    Pending,
}

let s = Status::Active;

So far, similar to Ruby Symbols. But the real power of Rust enums: each variant can carry data.

Data-carrying enums

enum Message {
    Quit,                       // no data
    Text(String),               // one String
    Move { x: i32, y: i32 },    // named fields
    Color(u8, u8, u8),          // multiple values
}

let msg = Message::Text(String::from("hello"));
let pos = Message::Move { x: 10, y: 20 };

In Ruby, you'd need class inheritance or Hashes for this. In Rust, one enum expresses "this type is one of these shapes."

match — forced exhaustive matching

Maps to Ruby's case/when, but you must handle every case.

# Ruby — no else is fine
case status
when :active then "active"
when :inactive then "inactive"
# missing :pending — no error
end

// Rust — missing case = compile error
match status {
    Status::Active => "active",
    Status::Inactive => "inactive",
    Status::Pending => "pending",  // omit this → error[E0004]
}

Why is this great? When you add a new variant to an enum, every match gets a compile error. You never miss an unhandled case.

Extracting data in match

match msg {
    Message::Quit => println!("quit"),
    Message::Text(text) => println!("text: {}", text),
    Message::Move { x, y } => println!("move: ({}, {})", x, y),
    Message::Color(r, g, b) => println!("color: {},{},{}", r, g, b),
}

Extract data from inside the enum while branching simultaneously. Like doing type checking and data extraction in one case/when.

if let — check just one case

While match handles all cases, if let checks just one.

if let Message::Text(text) = msg {
    println!("text: {}", text);
}
// rest is ignored

Feels like Ruby's if status == :active, but with data extraction included.

Option and Result are enums

Option<T> is Some(T) or None. Result<T, E> is Ok(T) or Err(E). Both are enums. Rust's error handling is built on top of enums.

Key Points

1

enum is Ruby Symbol groups evolved — can carry data

2

match is enhanced case/when — must handle every case exhaustively

3

match extracts data and branches simultaneously

4

Option and Result are enums — foundation of Rust error handling

Pros

  • Adding a variant makes the compiler find every unhandled location
  • Data-carrying enums are more concise than Ruby class inheritance

Cons

  • More boilerplate than Ruby Symbols for lightweight use
  • Forced exhaustive matching in match feels tedious at first

Use Cases

State machines — order status (pending/processing/done/cancelled) as types API responses — carrying different data for success/error/timeout