Menu
CoddyTech

Xem lại enum Option

Thuộc phần Lập trình hướng đối tượng trong hành trình Rust của Coddy. Bài học 17 trên 61.

Bạn đã gặp Option<T> trong Rust: đây là enum tiêu chuẩn dùng để biểu diễn các giá trị có thể tồn tại hoặc không. Bây giờ, hãy cùng nhìn nhận nó theo góc độ OOP: Option không chỉ là một enum, mà còn là một kiểu có các phương thức tích hợp mạnh mẽ, giúp bạn xử lý các giá trị có thể là null một cách an toàn và gọn gàng.

Thay vì kiểm tra mỗi lần, bạn có thể dùng trực tiếp các phương thức trên giá trị Option. Phương thức unwrap_or trả về giá trị bên trong nếu có, hoặc giá trị mặc định mà bạn cung cấp:

let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name);  // Alice

let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback);  // Guest

Phương thức map biến đổi giá trị bên trong nếu giá trị đó tồn tại, còn None thì giữ nguyên. Nhờ vậy, bạn có thể nối chuỗi các thao tác mà không phải liên tục kiểm tra None:

let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled);  // Some(10)

let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing);  // None

Các phương thức này thể hiện những nguyên tắc của OOP: kiểu Option đóng gói khái niệm “có thể có một giá trị” và cung cấp một giao diện rõ ràng để làm việc với nó. Bạn gọi phương thức trên đối tượng thay vì viết logic bên ngoài để kiểm tra đối tượng.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống hồ sơ người dùng xử lý dữ liệu tùy chọn một cách linh hoạt bằng các phương thức của Option. Người dùng có thể đã đặt biệt danh hoặc chưa. Hệ thống của bạn sẽ xử lý cả hai trường hợp một cách phù hợp mà không cần đối sánh tường minh.

Bạn sẽ tạo hai tệp để sắp xếp code:

  • profile.rs: Định nghĩa một struct công khai Profile với hai trường: username (String) và nickname (Option<String>). Triển khai:
    • Một hàm khởi tạo new nhận tên người dùng và một biệt danh tùy chọn
    • Một phương thức display_name trả về biệt danh nếu có, hoặc dùng tên người dùng làm giá trị dự phòng: sử dụng unwrap_or để xử lý việc này
    • Một phương thức formatted_nickname trả về một Option<String> với biệt danh được đặt trong dấu ngoặc vuông (như "[CoolNick]") nếu có, hoặc None nếu không có biệt danh: sử dụng map để biến đổi giá trị
  • main.rs: Đưa module hồ sơ vào và tạo hai hồ sơ: một hồ sơ có biệt danh và một hồ sơ không có biệt danh. Minh họa cả hai phương thức trên mỗi hồ sơ để cho thấy các phương thức của Option xử lý giá trị có mặt và vắng mặt khác nhau như thế nào.

Với phương thức formatted_nickname, hãy dùng định dạng debug ({:?}) khi in ra vì phương thức này trả về một Option.

Đầu ra của bạn phải theo đúng định dạng này, hiển thị cả hai hồ sơ:

Display name: {name}
Formatted nickname: {option}
Display name: {name}
Formatted nickname: {option}

Ví dụ, với một hồ sơ có tên người dùng "alice" và biệt danh "Ace", cùng một hồ sơ khác có tên người dùng "bob" nhưng không có biệt danh, đầu ra sẽ là:

Display name: Ace
Formatted nickname: Some("[Ace]")
Display name: bob
Formatted nickname: None

Bạn sẽ nhận ba đầu vào: tên người dùng thứ nhất, biệt danh của người dùng thứ nhất và tên người dùng thứ hai (người không có biệt danh).

Tự làm thử

mod profile;

use profile::Profile;

fn main() {
    // Đọc các đầu vào
    let mut input1 = String::new();
    std::io::stdin().read_line(&mut input1).expect("Failed to read line");
    let username1 = input1.trim().to_string();

    let mut input2 = String::new();
    std::io::stdin().read_line(&mut input2).expect("Failed to read line");
    let nickname1 = input2.trim().to_string();

    let mut input3 = String::new();
    std::io::stdin().read_line(&mut input3).expect("Failed to read line");
    let username2 = input3.trim().to_string();

    // TODO: Tạo hồ sơ đầu tiên với username1 và nickname1 (Some)
    
    // TODO: Tạo hồ sơ thứ hai với username2 và không có biệt danh (None)
    
    // TODO: In ra display_name và formatted_nickname của hồ sơ đầu tiên
    // Dùng {:?} cho formatted_nickname vì phương thức này trả về một Option
    
    // TODO: In ra display_name và formatted_nickname của hồ sơ thứ hai
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Rust online