Option trong Rust
Rust không có null. Nếu một giá trị có thể không có mặt, điều đó được thể hiện thẳng trong kiểu bằng Option. Lúc đầu có vẻ hơi rườm rà, nhưng nó loại bỏ cả một nhóm lỗi: “tham chiếu null”, thủ phạm của vô số lần sập chương trình ở các ngôn ngữ khác.
Option trông như thế nào
Phần tiêu đề “Option trông như thế nào”Option là một enum có hai biến thể (variant):
enum Option<T> { Some(T), // có một giá trị None, // không có giá trị}Thật ra bạn đã gặp nó rồi: .get() trên vector và .get() trên HashMap đều trả về một Option.
Tạo một Option
Phần tiêu đề “Tạo một Option”fn main() { let has_value: Option<i32> = Some(5); let no_value: Option<i32> = None;
println!("{:?}", has_value); println!("{:?}", no_value);}Kết quả:
Some(5)NoneXử lý Option bằng match
Phần tiêu đề “Xử lý Option bằng match”Cách an toàn để lấy giá trị ra là match. Rust buộc bạn xử lý cả hai trường hợp, nên bạn không thể vô tình quên trường hợp “không có”:
fn main() { let age: Option<i32> = Some(30);
match age { Some(value) => println!("Tuổi là {value}"), None => println!("Không có thông tin tuổi"), }}Một ví dụ thực tế
Phần tiêu đề “Một ví dụ thực tế”fn find_user(id: u32) -> Option<&'static str> { if id == 1 { Some("Alice") } else { None }}
fn main() { match find_user(1) { Some(name) => println!("Tìm thấy: {name}"), None => println!("Không tìm thấy người dùng"), }
match find_user(99) { Some(name) => println!("Tìm thấy: {name}"), None => println!("Không tìm thấy người dùng"), }}Kết quả:
Tìm thấy: AliceKhông tìm thấy người dùngThay vì trả về null hoặc ném ngoại lệ khi không tìm thấy người dùng, chính kiểu trả về của hàm đã nói với mọi nơi gọi nó rằng “có thể không tìm thấy gì”, và trình biên dịch không cho họ bỏ qua khả năng đó.
Lối tắt: unwrap_or và unwrap
Phần tiêu đề “Lối tắt: unwrap_or và unwrap”Với trường hợp đơn giản, viết cả một match có thể thấy hơi thừa. Có vài lối tắt:
fn main() { let age: Option<i32> = None;
let value = age.unwrap_or(0); // dùng 0 nếu không có giá trị println!("{value}");}Còn có .unwrap(), trả thẳng giá trị cho bạn, nhưng sẽ panic (sập chương trình) nếu đó là None:
let age: Option<i32> = Some(30);let value = age.unwrap(); // ổn ở đây, vì age là SomeLuyện tập
Phần tiêu đề “Luyện tập”1. Số chẵn đầu tiên
Phần tiêu đề “1. Số chẵn đầu tiên”Hãy viết fn first_even(numbers: &[i32]) -> Option<i32> trả về số chẵn đầu tiên trong slice, hoặc None nếu không có. In kết quả cho [3, 7, 8, 10] bằng match, và cho [1, 3, 5] bằng .unwrap_or(0). Gợi ý: duyệt slice và return Some(n) ngay khi n % 2 == 0; sau vòng lặp thì trả về None.
Xem lời giải
fn first_even(numbers: &[i32]) -> Option<i32> { for &n in numbers { if n % 2 == 0 { return Some(n); } } None}
fn main() { match first_even(&[3, 7, 8, 10]) { Some(n) => println!("Số chẵn đầu tiên: {n}"), None => println!("Không có số chẵn"), }
let fallback = first_even(&[1, 3, 5]).unwrap_or(0); println!("Có giá trị dự phòng: {fallback}");}Kết quả:
Số chẵn đầu tiên: 8Có giá trị dự phòng: 02. Tra cứu một thiết lập
Phần tiêu đề “2. Tra cứu một thiết lập”Hãy tạo một HashMap chứa các thiết lập, trong đó chỉ có "volume" được đặt là 80. Rồi duyệt qua các khoá "volume" và "brightness", in giá trị nếu có, hoặc in thông báo “chưa được đặt” nếu không có. Gợi ý: .get(key) trên HashMap trả về một Option, nên bạn có thể match trực tiếp trên nó.
Xem lời giải
use std::collections::HashMap;
fn main() { let mut settings = HashMap::new(); settings.insert("volume", 80);
for key in ["volume", "brightness"] { match settings.get(key) { Some(value) => println!("{key} = {value}"), None => println!("{key} chưa được đặt"), } }}Kết quả:
volume = 80brightness chưa được đặtTóm tắt
Phần tiêu đề “Tóm tắt”Option<T>biểu diễn một giá trị có thể có hoặc không: Rust không cónull.Some(value)chứa một thứ gì đó;Nonenghĩa là không có gì.matchlà cách an toàn để xử lý cả hai trường hợp; trình biên dịch không cho bạn quên trường hợp nào..unwrap_or(default)cho bạn một giá trị dự phòng;.unwrap()panic nếu giá trị làNone, nên hãy dùng thật dè dặt.
Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Kiểm tra nhanh
1. Rust dùng gì thay cho null với một giá trị có thể bị thiếu?
2. Chuyện gì xảy ra khi bạn gọi .unwrap() trên một giá trị None?
3. age.unwrap_or(0) làm gì khi age là None?
4. Vì sao match được coi là cách an toàn để lấy giá trị ra khỏi Option?
5. Những phương thức nào được nhắc đến trên trang này vốn đã trả về Option?
Điểm: 0 / 5
Đọc thêm
Phần tiêu đề “Đọc thêm”- The Option Enum and Its Advantages Over Null Values — The Rust Book (tiếng Anh): giải thích chính thức vì sao Rust dùng
Optionthay chonull. std::option— tài liệu thư viện chuẩn (tiếng Anh): tổng quan mọi thứ bạn làm được vớiOption, gồm các phương thức tiện lợi nhưmap,and_thenvàis_some.- Option & unwrap — Rust by Example (tiếng Anh): ví dụ ngắn chạy được về
match,unwrapvà khi nào panic xảy ra. Option<T>— tài liệu std (tiếng Anh): danh sách đầy đủ các phương thức, mỗi phương thức kèm một ví dụ nhỏ để bạn sao chép.