blog.dopana

Back

Tony Hoare, tác giả phát minh ra null, từng gọi phát minh này là “sai lầm tỷ đô” vì lỗi giải tham chiếu con trỏ null (null pointer dereference) đã gây ra vô số vụ sập hệ thống và lỗ hổng bảo mật. Rust giải quyết triệt để vấn đề này ngay tại thời điểm biên dịch bằng kiểu enum Option<T> cùng biến thể Some(T).

Explain Like I’m 10: Hộp Quà Bí Ẩn#

Hãy tưởng tượng bạn nhận được một chiếc hộp chuyển phát nhanh:

  1. Some(Quà): Bạn bóc lớp giấy bọc và thấy một món đồ chơi thật sự bên trong.
  2. None: Chiếc hộp hoàn toàn rỗng không có gì.
graph TD
    Box["Option&lt;T&gt; (Chiếc Hộp Chuyển Phát)"] --> Check{"Có món đồ bên trong không?"}
    Check -->|Có: Tồn tại giá trị| SomeBranch["Some(giá_trị)<br/>(Chứa dữ liệu thật kiểu T)"]
    Check -->|Không: Rỗng| NoneBranch["None<br/>(Không có giá trị nào)"]
    SomeBranch --> Unbox["Mở hộp an toàn bằng match, if let, hoặc combinator"]
    NoneBranch --> Fallback["Xử lý trường hợp rỗng mà không bị crash app"]

Trong nhiều ngôn ngữ khác, bạn có thể vô tình thao tác lên một biến rỗng (như gọi user.getName() khi usernull), khiến chương trình sập runtime ngay lập tức. Trong Rust, bạn không thể dùng trực tiếp dữ liệu bên trong Some(T) nếu chưa chủ động “mở hộp”.

Cách Rust Định Nghĩa Option<T>#

Option<T> được tích hợp sẵn trong prelude chuẩn của Rust, do đó Option, SomeNone luôn sẵn sàng để sử dụng:

enum Option<T> {
    Some(T),
    None,
}
rust

Option<T>T là hai kiểu dữ liệu hoàn toàn khác biệt, trình biên dịch sẽ không bao giờ cho phép bạn dùng nhầm một biến Option<i8> vào nơi yêu cầu kiểu i8:

let x: i8 = 5;
let y: Option<i8> = Some(5);

// let sum = x + y; // ❌ Lỗi biên dịch: không thể cộng `Option<i8>` với `i8`
rust

4 Cách Chuẩn Mực Để Xử Lý Some()#

1. Khớp Mẫu Với match (Tường minh & Bao quát 100%)#

2. Rút Gọn Cú Pháp Với if let#

Khi bạn chỉ quan tâm đến trường hợp có dữ liệu Some và bỏ qua trường hợp None:

src/main.rs
let config_timeout: Option<u64> = Some(3000);

if let Some(timeout) = config_timeout {
    println!("Thời gian chờ: {timeout}ms");
}
rust

3. Thiết Lập Giá Trị Fallback Mặc Định: unwrap_orunwrap_or_else#

Hạn chế dùng .unwrap() trần trụi trong code production. Thay vào đó, hãy luôn cung cấp giá trị mặc định:

src/main.rs
let port: Option<u16> = None;

// Cung cấp giá trị dự phòng ngay lập tức
let active_port = port.unwrap_or(8080);
println!("Server lắng nghe trên cổng: {active_port}"); // 8080

// Cung cấp giá trị dự phòng được tính toán lazy qua closure
let env_port = port.unwrap_or_else(|| {
    // Chỉ tính toán khi port là None
    4000 + 4000
});
println!("Cổng dự phòng: {env_port}");
rust

4. Biến Đổi Dữ Liệu Theo Phong Cách Functional Với Combinator#

Bạn có thể biến đổi giá trị bên trong Some mà không cần unwrap thủ công:

src/main.rs
let raw_input: Option<&str> = Some("   rustacean   ");

let cleaned = raw_input
    .map(|s| s.trim())
    .filter(|s| !s.is_empty())
    .map(|s| s.to_uppercase());

println!("{cleaned:?}"); // Some("RUSTACEAN")
rust
graph LR
    Input["Some('   rustacean   ')"] -->|map trim| Trimmed["Some('rustacean')"]
    Trimmed -->|filter not empty| Filtered["Some('rustacean')"]
    Filtered -->|map uppercase| Final["Some('RUSTACEAN')"]

So Sánh Null Trong Các Ngôn Ngữ Khác vs Option Trong Rust#

Ngôn ngữCơ chế biểu diễnMức độ an toànHậu quả khi quên kiểm tra
JavaScript / TypeScriptnull / undefinedThấpRuntime TypeError sập app
Java / C#Con trỏ nullTrung bình - ThấpNullPointerException
PythonNoneThấpAttributeError: 'NoneType'
RustOption<T> (Some hoặc None)An toàn tuyệt đối 100%Báo lỗi ngay lúc biên dịch

Tổng kết#

  • Some(giá_trị) đại diện cho sự hiện diện của một giá trị hợp lệ kiểu T trong Option<T>.
  • None đại diện cho sự vắng mặt hoàn toàn của dữ liệu.
  • Rust thay thế null bằng Option<T>, loại bỏ hoàn toàn các lỗi sập app runtime do null pointer.
  • Dùng match để kiểm tra bao quát, if let khi chỉ xử lý 1 nhánh, và .unwrap_or() hoặc .map() để nối chuỗi xử lý functional gọn gàng.

Tài liệu tham khảo#