An enum (short for enumeration) lets you define a type by listing its possible variants. Each variant is a valid value of that type.
Enum (viết tắt của enumeration) cho phép bạn định nghĩa một kiểu dữ liệu bằng cách liệt kê các biến thể có thể có của nó. Mỗi biến thể là một tên hợp lệ trong kiểu đó.
1enum Direction {2 North,3 South,4 East,5 West,6}78fn main() {9 let dir = Direction::North;1011 match dir {12 Direction::North => println!("Heading north"),13 Direction::South => println!("Heading south"),14 Direction::East => println!("Heading east"),15 Direction::West => println!("Heading west"),16 }17}Enum variants are accessed with the double-colon (::) notation. Enums carry no data by default but are still first-class types in Rust's type system.
Biến thể enum được truy cập bằng ký hiệu hai dấu hai chấm (::). Enum không mang dữ liệu theo mặc định nhưng vẫn là kiểu dữ liệu đầy đủ trong hệ thống kiểu của Rust.
Không giống C hay C++, các biến thể enum của Rust không phải là số nguyên theo mặc định. Chúng là các kiểu có tên đầy đủ và an toàn về kiểu.
The real power of Rust enums is that each variant can carry its own data — like a typed union or a sum type in type theory.
Sức mạnh thực sự của enum trong Rust là mỗi biến thể có thể mang dữ liệu của riêng nó — giống như một union được đánh kiểu hoặc một kiểu tổng (sum type) trong lý thuyết kiểu.
1enum Shape {2 Circle(f64), // radius3 Rectangle(f64, f64), // width, height4 Triangle { base: f64, height: f64 }, // named fields5}67fn area(shape: &Shape) -> f64 {8 match shape {9 Shape::Circle(r) => std::f64::consts::PI * r * r,10 Shape::Rectangle(w, h) => w * h,11 Shape::Triangle { base, height } => 0.5 * base * height,12 }Variants can hold anonymous tuples, named struct fields, or nothing at all. This makes Rust enums far more expressive than enums in most other languages.
Biến thể có thể chứa các tuple ẩn danh, cấu trúc có tên, hoặc không có gì. Điều này khiến enum của Rust trở nên linh hoạt hơn nhiều so với enum trong hầu hết các ngôn ngữ khác.
Rust has no null. Instead it uses the standard library's Option<T> type to represent a value that may or may not be present. This forces you to handle absence explicitly.
Rust không có giá trị null. Thay vào đó, nó sử dụng kiểu Option<T> từ thư viện chuẩn để biểu diễn một giá trị có thể có hoặc không có. Điều này buộc bạn phải xử lý trường hợp vắng mặt một cách tường minh.
1// Option is defined in std as:2// enum Option<T> {3// Some(T),4// None,5// }67fn divide(a: f64, b: f64) -> Option<f64> {8 if b == 0.0 {9 None10 } else {11 Some(a / b)12 }Some và None được nhập sẵn vào phạm vi (prelude), vì vậy bạn không cần viết Option::Some hay Option::None — chỉ cần Some và None là đủ.
The match expression compares a value against a series of patterns and executes the first arm that matches. Rust's compiler requires match to be exhaustive — you must cover every possible variant.
Biểu thức match so sánh một giá trị với một loạt các mẫu và thực thi nhánh đầu tiên khớp. Trình biên dịch Rust yêu cầu match phải toàn diện — bạn phải bao phủ mọi biến thể có thể xảy ra.
1#[derive(Debug)]2enum Coin {3 Penny,4 Nickel,5 Dime,6 Quarter(String), // State quarter with state name7}89fn value_in_cents(coin: &Coin) -> u32 {10 match coin {11 Coin::Penny => {12 println!("Lucky penny!");Each match arm has the form pattern => expression. If the body is multiple statements, wrap it in braces. The value of the matched arm becomes the value of the entire match expression.
Mỗi nhánh match có dạng pattern => expression. Nếu phần thân là nhiều câu lệnh, hãy đặt trong dấu ngoặc nhọn. Giá trị của nhánh khớp cuối cùng trở thành giá trị của toàn bộ biểu thức match.
Rust supports many powerful pattern forms inside match expressions: numeric ranges, multiple patterns at once, @ bindings, and guard conditions.
Rust hỗ trợ nhiều loại mẫu mạnh mẽ bên trong biểu thức match: phạm vi số, nhiều mẫu cùng lúc, binding với @, và điều kiện bảo vệ (guard).
1fn classify(n: i32) -> &'static str {2 match n {3 i32::MIN..=-1 => "negative",4 0 => "zero",5 1..=9 => "single digit",6 10..=99 => "double digit",7 _ => "large",8 }9}1011fn describe_pair(pair: (i32, i32)) -> String {12 match pair {The _ pattern matches any value without binding it to a variable. It is commonly used as the final catch-all arm in a match to satisfy exhaustiveness.
Mẫu _ khớp với bất kỳ giá trị nào mà không ràng buộc nó với một biến. Nó thường được dùng như nhánh mặc định cuối cùng trong match để đảm bảo tính toàn diện.
Sometimes you only care about one variant. The if let syntax lets you match one pattern and ignore all other cases without a full match block.
Đôi khi bạn chỉ quan tâm đến một biến thể duy nhất. Cú pháp if let cho phép bạn khớp một mẫu và bỏ qua tất cả các trường hợp còn lại mà không cần viết toàn bộ khối match.
1fn main() {2 let config_max = Some(3u8);34 // Verbose match5 match config_max {6 Some(max) => println!("Max is {max}"),7 None => (),8 }910 // Equivalent, more concise if let11 if let Some(max) = config_max {12 println!("Max is {max}");if let mất tính toàn diện mà bạn có được từ match. Dùng nó khi bạn thực sự chỉ quan tâm đến một trường hợp; dùng match khi logic của bạn phụ thuộc vào nhiều biến thể.
Enum variants can contain other enums, creating complex, structured data types that accurately reflect your domain model.
Các biến thể enum có thể chứa các enum khác, tạo ra các kiểu dữ liệu phức tạp, có cấu trúc phản ánh mô hình miền của bạn một cách chính xác.
1#[derive(Debug)]2enum Color {3 Rgb(u8, u8, u8),4 Named(NamedColor),5}67#[derive(Debug)]8enum NamedColor {9 Red,10 Green,11 Blue,12 Custom(String),Enums appear everywhere in Rust code. Here are the most common patterns you will encounter and use daily.
Enum xuất hiện ở khắp nơi trong code Rust. Dưới đây là các mẫu phổ biến nhất mà bạn sẽ gặp và sử dụng hàng ngày.
1#[derive(Debug, PartialEq)]2enum TrafficLight {3 Red,4 Yellow,5 Green,6}78impl TrafficLight {9 fn next(&self) -> Self {10 match self {11 TrafficLight::Red => TrafficLight::Green,12 TrafficLight::Green => TrafficLight::Yellow,1#[derive(Debug)]2enum AppEvent {3 KeyPress(char),4 MouseClick { x: i32, y: i32 },5 Resize(u32, u32),6 Quit,7}89fn handle(event: AppEvent) {10 match event {11 AppEvent::KeyPress(c) => println!("Key: {c}"),12 AppEvent::MouseClick { x, y } => println!("Click at ({x}, {y})"),- Use enums when a value can be one of several distinct variants
Dùng enum khi một giá trị có thể là một trong nhiều biến thể khác nhau
- Use match for exhaustive handling of all possible variants
Dùng match để xử lý toàn diện mọi biến thể có thể xảy ra
- Use if let when you only care about a single variant
Dùng if let khi bạn chỉ quan tâm đến một biến thể duy nhất
- Avoid panicking unwraps on Option — match or use unwrap_or instead
Tránh panic khi unwrap Option — hãy khớp mẫu hoặc dùng unwrap_or
- Implement methods on enums to encapsulate related behavior
Triển khai các phương thức trên enum để đóng gói hành vi liên quan
Key Takeaways
Điểm Chính
- Enums define types with multiple named variantsEnum định nghĩa kiểu với nhiều biến thể có tên
- Option<T> replaces null with Some(value) and NoneOption<T> thay thế null bằng Some(giá trị) và None
- match expressions must handle every possible variantBiểu thức match phải xử lý mọi biến thể có thể
- if let is a shorthand for matching a single patternif let là cách viết tắt để khớp một mẫu duy nhất
Practice
Test your understanding of this chapter
What is the correct syntax to access an enum variant in Rust?
Cú pháp đúng để truy cập một biến thể enum trong Rust là gì?
What does the None variant of Option<T> represent?
Biến thể None của Option<T> đại diện cho điều gì?
Rust's match expression must be exhaustive — it must cover every possible variant of the matched type.
Biểu thức match trong Rust phải toàn diện — nó phải bao phủ mọi biến thể có thể có của kiểu được khớp.
Rust enum variants are integers by default, just like in C or C++.
Các biến thể enum trong Rust là số nguyên theo mặc định, giống như trong C hay C++.
Extract a value from Option using if let
Trích xuất giá trị từ Option bằng if let
let x: Option<i32> = Some(42); if let (value) = x { println!("{value}"); }