ข้ามไปยังเนื้อหา

Enum แบบ Algebraic Data Type

TypeScript มี union type ("circle" | "rectangle") และ discriminated union (object ที่มี field kind) พวกนี้ทรงพลัง แต่เป็น convention — compiler เชื่อว่าคุณ set discriminant ถูกต้อง

Rust enum เป็น algebraic data type (ADT) แต่ละ variant พก data ของตัวเองได้ — ต่าง type ต่าง variant Compiler บังคับ exhaustive pattern matching: ถ้าคุณเพิ่ม variant ใหม่ ทุก match ในโค้ดที่ handle enum นั้นต้องอัปเดตด้วย มิฉะนั้นโค้ดจะ compile ไม่ได้

TypeScript
// TypeScript — discriminated union (convention-based)
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number }
| { kind: "triangle"; base: 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;
case "triangle": return 0.5 * shape.base * shape.height;
// ถ้าเพิ่ม variant แล้วลืม case TypeScript
// จะ warn เฉพาะเมื่อใช้ never-check pattern
}
}
Rust
#[derive(Debug)]
enum Shape {
Circle(f64),
Rectangle(f64, f64),
Triangle { base: f64, height: f64 },
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle { base, height } => 0.5 * base * height,
}
}
}
fn main() {
let shapes = vec![
Shape::Circle(3.0),
Shape::Rectangle(4.0, 5.0),
Shape::Triangle { base: 6.0, height: 4.0 },
];
for s in &shapes {
println!("{:?} => area = {:.2}", s, s.area());
}
}

Enum เป็นวิธี idiomatic ในการ model state machine ใน Rust แต่ละ state variant พก data ที่เกี่ยวข้องกับ state นั้นเท่านั้น:

TypeScript
// TypeScript — state machine ด้วย discriminated union
type ConnectionState =
| { status: "disconnected" }
| { status: "connecting"; attempt: number }
| { status: "connected"; sessionId: string };
function describe(state: ConnectionState): string {
switch (state.status) {
case "disconnected": return "not connected";
case "connecting": return `attempt ${state.attempt}`;
case "connected": return `session ${state.sessionId}`;
}
}
Rust
enum ConnectionState {
Disconnected,
Connecting { attempt: u32 },
Connected { session_id: String },
}
fn describe(state: &ConnectionState) -> String {
match state {
ConnectionState::Disconnected => "not connected".to_string(),
ConnectionState::Connecting { attempt } => format!("attempt {}", attempt),
ConnectionState::Connected { session_id } => format!("session {}", session_id),
}
}
fn main() {
let states = vec![
ConnectionState::Disconnected,
ConnectionState::Connecting { attempt: 3 },
ConnectionState::Connected { session_id: "abc-123".to_string() },
];
for s in &states {
println!("{}", describe(s));
}
}
#[derive(Debug)]
enum Shape {
Circle(f64),
Rectangle(f64, f64),
Triangle { base: f64, height: f64 },
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle { base, height } => 0.5 * base * height,
}
}
}
fn main() {
let shapes = vec![
Shape::Circle(3.0),
Shape::Rectangle(4.0, 5.0),
Shape::Triangle { base: 6.0, height: 4.0 },
];
for s in &shapes {
println!("{:?} => area = {:.2}", s, s.area());
}
}
เกิดอะไรขึ้นใน Rust ถ้า `match` ไม่ครอบคลุมทุก enum variant?
Feature ของ ADT ที่ Rust enum มีแต่ TypeScript enum ไม่มีคืออะไร?
ใน Rust `match` arm `_` ทำอะไร?