Menu
CoddyTech

Khớp các biến thể 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 16 trên 61.

Trong bài học trước, bạn đã dùng match bên trong các phương thức của enum để xử lý các biến thể khác nhau. Giờ hãy cùng tìm hiểu cách match trích xuất dữ liệu được lưu bên trong các biến thể đó: kỹ thuật này gọi là phân rã cấu trúc (destructuring).

Khi khớp với một biến thể enum chứa dữ liệu, bạn có thể liên kết dữ liệu đó với các biến ngay trong mẫu:

enum Event {
    Click { x: i32, y: i32 },
    KeyPress(char),
    Resize(u32, u32),
}

fn handle_event(event: Event) {
    match event {
        Event::Click { x, y } => {
            println!("Clicked at ({}, {})", x, y);
        }
        Event::KeyPress(key) => {
            println!("Key pressed: {}", key);
        }
        Event::Resize(width, height) => {
            println!("Resized to {}x{}", width, height);
        }
    }
}

Mỗi nhánh trích xuất dữ liệu theo cách khác nhau, tùy vào cấu trúc của biến thể. Với các biến thể dạng struct, bạn dùng { field_name } để lấy các trường có tên. Với các biến thể dạng tuple, bạn dùng các biến theo vị trí như (width, height).

Tên biến (variable) bạn chọn sẽ khả dụng bên trong nhánh match (match arm) đó. Điều này rất hữu ích vì bạn vừa kiểm tra xem mình có biến thể (variant) nào, vừa truy cập được dữ liệu đi kèm (payload) của biến thể đó.

Trình biên dịch đảm bảo bạn xử lý mọi biến thể, và các giá trị đã trích xuất có thể dùng ngay: không cần thêm bước nào.

challenge icon

Thử thách

Dễ

Hãy xây dựng một bộ xử lý lệnh xử lý các loại lệnh khác nhau của người dùng. Mỗi lệnh mang dữ liệu riêng, và bộ xử lý sẽ dùng khớp mẫu để trích xuất dữ liệu đó rồi tạo phản hồi phù hợp.

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

  • command.rs: Định nghĩa enum công khai Command với ba biến thể:
    • Say: chứa một thông điệp String duy nhất (cú pháp tuple)
    • Move: chứa các trường có tên: direction (String) và steps (u32)
    • Calculate: chứa hai giá trị i32 đại diện cho các số cần cộng (cú pháp tuple)
    Sau đó, triển khai phương thức execute, dùng match để phân rã từng biến thể và trả về một String mô tả hành động đã thực hiện.
  • main.rs: Đưa module lệnh vào, tạo một instance cho mỗi biến thể lệnh bằng các đầu vào đã cho, rồi gọi phương thức execute trên từng biến thể để in kết quả.

Phương thức execute cần phân rã từng biến thể để truy cập dữ liệu và trả về thông điệp theo đúng các định dạng sau:

  • Với Say: Saying: {message}
  • Với Move: Moving {steps} steps {direction}
  • Với Calculate: {a} + {b} = {sum}

Đầu ra cần hiển thị kết quả của từng lệnh trên một dòng riêng:

Saying: {message}
Moving {steps} steps {direction}
{a} + {b} = {sum}

Ví dụ, với thông điệp Say là "Hello world", lệnh di chuyển 5 bước theo hướng "north", và phép tính với hai số 10 và 25, đầu ra sẽ là:

Saying: Hello world
Moving 5 steps north
10 + 25 = 35

Bạn sẽ nhận năm đầu vào: thông điệp Say, hướng di chuyển, số bước và hai số cần tính.

Tự làm thử

mod command;

use command::Command;

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

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

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

    let mut num1_input = String::new();
    std::io::stdin().read_line(&mut num1_input).expect("Failed to read line");
    let num1: i32 = num1_input.trim().parse().expect("Failed to parse num1");

    let mut num2_input = String::new();
    std::io::stdin().read_line(&mut num2_input).expect("Failed to read line");
    let num2: i32 = num2_input.trim().parse().expect("Failed to parse num2");

    // TODO: Tạo lệnh Say bằng say_message rồi thực thi lệnh đó
    
    // TODO: Tạo lệnh Move bằng direction và steps rồi thực thi lệnh đó
    
    // TODO: Tạo lệnh Calculate bằng num1 và num2 rồi thực thi lệnh đó
    
    // TODO: In ra kết quả thực thi của từng lệnh
}
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