Menu
CoddyTech

Enum có dữ liệu

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 14 trên 61.

Enum cơ bản (enum) trong Rust cho phép bạn định nghĩa một kiểu dữ liệu với tập hợp biến thể cố định, như Direction::North hoặc Status::Active. Nhưng enum trong Rust còn mạnh hơn thế nhiều. Mỗi biến thể có thể chứa dữ liệu riêng, giúp enum cực kỳ linh hoạt khi mô hình hóa các tình huống thực tế.

Hãy hình dung một hệ thống nhắn tin, trong đó mỗi loại tin nhắn mang thông tin khác nhau. Tin nhắn văn bản có nội dung, lệnh di chuyển có tọa độ, còn tín hiệu thoát thì không chứa gì cả. Trong Rust, bạn có thể biểu diễn tất cả những loại này bằng một enum duy nhất:

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(u8, u8, u8),
}

Mỗi biến thể có hình dạng khác nhau. Quit không chứa dữ liệu: nó chỉ là một tín hiệu.

Move dùng cú pháp dạng struct với các trường được đặt tên. Write chứa một String duy nhất theo cú pháp tuple. ChangeColor chứa ba giá trị biểu thị các thành phần RGB.

Cách tạo đối tượng của các biến thể này như sau:

let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
let m3 = Message::Write(String::from("Hello"));
let m4 = Message::ChangeColor(255, 128, 0);

Điểm hay của cách tiếp cận này là m1, m2, m3 và m4 đều có cùng kiểu dữ liệu: Message. Bạn có thể lưu chúng trong cùng một tập hợp, truyền chúng vào cùng một hàm hoặc trả về bất kỳ biến thể nào từ một hàm duy nhất. Enum hợp nhất các cấu trúc dữ liệu khác nhau dưới cùng một kiểu dữ liệu mà vẫn giữ nguyên thông tin riêng cần thiết cho từng biến thể.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống thông báo dùng enum có dữ liệu để biểu thị các loại cảnh báo khác nhau. Mỗi loại thông báo mang thông tin khác nhau: một số có thông điệp, một số có giá trị số, còn một số chỉ là tín hiệu đơn giản.

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

  • notification.rs: Định nghĩa một enum Notification công khai với ba biến thể:
    • Alert: chứa một thông điệp String (cú pháp tuple)
    • Reminder: chứa các trường có tên: title (String) và minutes (u32)
    • Dismiss: không chứa dữ liệu, chỉ là một tín hiệu
  • main.rs: Đưa module thông báo vào và tạo một đối tượng cho mỗi biến thể. Sau đó, in thông tin về từng thông báo để cho thấy cả ba biến thể đều cùng kiểu nhưng mang dữ liệu khác nhau.

Khi in, hãy dùng định dạng gỡ lỗi ({:?}) để hiển thị từng thông báo. Bạn cần dẫn xuất trait Debug cho enum để có thể in theo cách này.

Đầu ra của bạn cần hiển thị cả ba thông báo, mỗi thông báo trên một dòng riêng:

Alert("{message}")
Reminder { title: "{title}", minutes: {minutes} }
Dismiss

Ví dụ, với thông điệp cảnh báo là "Server down", lời nhắc có tiêu đề "Meeting" và 30 phút, đầu ra sẽ là:

Alert("Server down")
Reminder { title: "Meeting", minutes: 30 }
Dismiss

Bạn sẽ nhận ba đầu vào: thông điệp cảnh báo, tiêu đề lời nhắc và số phút nhắc.

Tự làm thử

mod notification;

use notification::Notification;

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

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

    let mut minutes_input = String::new();
    std::io::stdin().read_line(&mut minutes_input).expect("Failed to read line");
    let reminder_minutes: u32 = minutes_input.trim().parse().expect("Failed to parse minutes");

    // TODO: Tạo thông báo Alert bằng alert_message

    // TODO: Tạo thông báo Reminder bằng reminder_title và reminder_minutes

    // TODO: Tạo thông báo Dismiss

    // TODO: In ra từng thông báo bằng định dạng gỡ lỗi {:?}
    // In mỗi thông báo trên một dòng riêng
}
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