Skip to content

Enums and Match

TypeScript has discriminated unions: a type union where each member has a literal kind field that narrows the type. Rust enums are first-class: each variant is a named type that can carry data directly, with no manual kind field needed.

In TypeScript you write a union of object types. In Rust, each enum variant can hold tuple-style data, named fields, or nothing at all:

// TypeScript discriminated union
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number };
// Rust enum with data variants
enum Shape {
Circle(f64),
Rectangle(f64, f64),
}

When you match on a Rust enum, the compiler requires you to handle every variant. Forget one and the build fails.

if let is shorthand for a match with one arm you care about and a fallback you don’t:

if let Shape::Circle(r) = shape {
println!("radius is {r}");
}
TypeScript
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "rectangle": return shape.width * shape.height;
// TypeScript does not enforce exhaustiveness by default
}
}
Rust
enum Shape {
Circle(f64),
Rectangle(f64, f64),
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
// forgetting a variant is a compile error
}
}
enum Shape {
Circle(f64),
Rectangle(f64, f64),
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
}
}
fn main() {
let c = Shape::Circle(5.0);
let r = Shape::Rectangle(4.0, 6.0);
println!("Circle area: {:.2}", area(&c));
println!("Rectangle area: {:.2}", area(&r));
}
What happens if you miss a variant in a Rust `match` on an enum?
What is `if let` in Rust?
Which Rust enum variant syntax carries named fields?