🎁 Option: Có Hoặc Không Có
🎯 Mục Tiêu Bài Học
Sau khi hoàn thành bài học này, bạn sẽ:
- ✅ Hiểu được
Option<T>là gì và tại sao không cónull - ✅ Sử dụng
Some(T)vàNone - ✅ Pattern matching với
Option - ✅ Dùng methods:
unwrap(),expect(),unwrap_or() - ✅ Áp dụng
map(),and_then(),filter() - ✅ Xử lý
Optionan toàn trong code thực tế
🤔 Option Là Gì?
Ẩn Dụ Cuộc Sống: Hộp Quà Bí Ẩn
Option giống như hộp quà có thể rỗng:
🎁 Hộp Quà:
- Có thể chứa quà (Some) → Mở ra thấy món đồ
- Có thể rỗng (None) → Mở ra không có gì
- Phải kiểm tra trước khi dùng
🎁 Option<T> Trong Rust:
Some(value)→ Có giá trịNone→ Không có gì- Phải xử lý cả 2 cases
Tại Sao Không Có null?
Nhiều ngôn ngữ có null:
// JavaScript - Lỗi runtime!
let user = null;
console.log(user.name); // TypeError: Cannot read property 'name' of null
Vấn đề với null:
- ❌ Dễ quên kiểm tra
- ❌ Lỗi runtime khó debug
- ❌ "Billion-dollar mistake" - Tony Hoare
Rust dùng Option:
- ✅ Compiler bắt buộc xử lý
- ✅ Lỗi bắt được lúc compile
- ✅ Type-safe
📦 Định Nghĩa Option
enum Option<T> {
Some(T),
None,
}
Giải thích:
Option<T>→ Generic enumSome(T)→ Chứa giá trị kiểuTNone→ Không có giá trị
Tạo Option
fn main() {
// Some - có giá trị
let some_number: Option<i32> = Some(5);
let some_string: Option<String> = Some(String::from("Hello"));
// None - không có giá trị
let absent_number: Option<i32> = None;
println!("{:?}", some_number);
println!("{:?}", absent_number);
}
Đầu ra:
Some(5)
None
🎯 Pattern Matching
Match Expression
fn main() {
let x: Option<i32> = Some(5);
match x {
Some(value) => println!("Có giá trị: {}", value),
None => println!("Không có gì"),
}
let y: Option<i32> = None;
match y {
Some(value) => println!("Có giá trị: {}", value),
None => println!("Không có gì"),
}
}
Đầu ra:
Có giá trị: 5
Không có gì
If Let
fn main() {
let number = Some(7);
// If let - chỉ quan tâm case Some
if let Some(n) = number {
println!("Số: {}", n);
} else {
println!("Không có số");
}
}
While Let
fn main() {
let mut stack = vec![1, 2, 3, 4, 5];
while let Some(top) = stack.pop() {
println!("{}", top);
}
}
Đầu ra:
5
4
3
2
1
🛠️ Methods Cơ Bản
unwrap() - Lấy Giá Trị (Nguy Hiểm!)
fn main() {
let x = Some(5);
println!("{}", x.unwrap()); // OK: 5
let y: Option<i32> = None;
// println!("{}", y.unwrap()); // ❌ PANIC!
}
Cảnh báo: Chỉ dùng khi chắc chắn có giá trị!
expect() - Unwrap Với Message
fn main() {
let x = Some("value");
println!("{}", x.expect("Phải có giá trị!"));
let y: Option<&str> = None;
// println!("{}", y.expect("Lỗi: Không có giá trị!")); // PANIC với message
}
is_some() và is_none()
fn main() {
let x = Some(2);
if x.is_some() {
println!("x có giá trị");
}
let y: Option<i32> = None;
if y.is_none() {
println!("y không có giá trị");
}
}
unwrap_or() - Giá Trị Mặc Định
fn main() {
let x = Some(5);
println!("{}", x.unwrap_or(0)); // 5
let y: Option<i32> = None;
println!("{}", y.unwrap_or(0)); // 0 (default)
}
Đầu ra:
5
0
unwrap_or_else() - Giá Trị Từ Closure
fn main() {
let x: Option<i32> = None;
let value = x.unwrap_or_else(|| {
println!("Tính toán giá trị mặc định");
100
});
println!("{}", value);
}
Đầu ra:
Tính toán giá trị mặc định
100
unwrap_or_default() - Default Trait
fn main() {
let x: Option<i32> = None;
println!("{}", x.unwrap_or_default()); // 0
let y: Option<String> = None;
println!("{}", y.unwrap_or_default()); // ""
}