Enums and Pattern Matching
Enums with data
Rust enums can carry data (like tagged unions). Each variant can have its own type.
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}In C#: Closest is a discriminated union (F#) or a class hierarchy; C# enums are just integers. Rust enums are sum types: one of several variants, each possibly with payload.
Option
Option<T> represents an optional value: either Some(T) or None. No null for regular references.
fn find_index(v: &[i32], x: i32) -> Option<usize> {
for (i, &n) in v.iter().enumerate() {
if n == x {
return Some(i);
}
}
None
}In C#: Like T? (nullable value types) or T that might be null; in Rust the type system forces you to handle None.
Result
Result<T, E> is either Ok(T) or Err(E), used for fallible operations instead of exceptions.
fn parse_number(s: &str) -> Result<i32, std::num::ParseIntError> {
s.parse()
}In C#: Like returning a value or throwing; in Rust you return Result and the caller must handle Err.
match
match exhaustively matches on an enum (or other types). Every case must be handled.
let opt: Option<i32> = Some(5);
match opt {
Some(n) => println!("value is {}", n),
None => println!("no value"),
}In C#: Like switch on a type, but exhaustive; no fall-through. Rust has no implicit fall-through.
Pattern matching
You can destructure in patterns:
match msg {
Message::Quit => {}
Message::Move { x, y } => println!("move to {}, {}", x, y),
Message::Write(s) => println!("write: {}", s),
Message::ChangeColor(r, g, b) => println!("color {}, {}, {}", r, g, b),
}In C#: Similar to pattern matching in C# 8+ switch expressions; Rust match is an expression and returns a value.
if let
When you only care about one variant:
if let Some(n) = opt {
println!("got {}", n);
}In C#: Like if (x is SomeType n) { ... }.
Key takeaway
Enums carry data; Option and Result replace null and exceptions. Use match for exhaustive handling and if let for a single variant.