Menu
CoddyTech

Nhiều ràng buộc

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

Đôi khi, một trait bound là chưa đủ. Bạn có thể cần một kiểu tổng quát vừa có thể được in ra vừa cung cấp bản tóm tắt. Rust cho phép bạn yêu cầu nhiều trait bằng cú pháp +.

Sau đây là cách chỉ định rằng một kiểu phải triển khai hai trait:

use std::fmt::Display;

trait Summary {
    fn summarize(&self) -> String;
}

fn announce<T: Display + Summary>(item: T) {
    println!("Breaking news: {}", item);
    println!("Summary: {}", item.summarize());
}

Ràng buộc T: Display + Summary có nghĩa là “T phải triển khai cả Display lẫn Summary”. Bên trong hàm, bạn có thể dùng các khả năng của cả hai trait: in ra bằng {} (từ Display) và gọi summarize() (từ Summary).

Bạn có thể kết hợp bao nhiêu trait tùy ý:

fn process<T: Display + Summary + Clone>(item: T) {
    // Có thể in ra, tóm tắt VÀ clone
}

Mẫu này rất cần thiết khi hàm của bạn dựa vào nhiều hành vi. Thay vì chấp nhận mọi kiểu dữ liệu rồi hy vọng mọi thứ hoạt động, bạn khai báo rõ những khả năng cần thiết, còn trình biên dịch sẽ kiểm tra chúng ngay trong quá trình biên dịch.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống kiểm tra sản phẩm, yêu cầu các mặt hàng có nhiều khả năng! Bạn sẽ tạo một hàm tổng quát chỉ chấp nhận các kiểu triển khai cả một trait tùy chỉnh lẫn một trait tiêu chuẩn, qua đó cho thấy cú pháp + kết hợp nhiều ràng buộc như thế nào.

Bạn sẽ sắp xếp code trong hai tệp:

  • product.rs: Định nghĩa trait công khai Inspectable với phương thức inspect, nhận &self và trả về một String chứa thông tin kiểm tra. Sau đó, tạo struct công khai Gadget với các trường công khai name (String) và serial (u32). Gadget của bạn cần triển khai cả Inspectable (trả về Inspecting: {name}) và std::fmt::Display (định dạng thành {name} (SN: {serial})). Cuối cùng, tạo một hàm tổng quát công khai có tên full_report, chấp nhận mọi kiểu T triển khai cả Display lẫn Inspectable. Hàm này cần in ra hai dòng: đầu tiên là mặt hàng bằng bộ định dạng {}, sau đó là kết quả gọi inspect().
  • main.rs: Đưa module product vào và tạo một instance Gadget bằng các đầu vào đã cho. Gọi full_report với gadget của bạn để cho thấy nó đáp ứng cả hai yêu cầu về trait.

Sức mạnh của nhiều ràng buộc là hàm full_report có thể dùng khả năng từ cả hai trait, vừa hiển thị mặt hàng đẹp mắt vừa lấy thông tin kiểm tra, tất cả đều được bảo đảm tại thời điểm biên dịch.

Đầu ra của bạn cần hiển thị cả định dạng hiển thị lẫn kết quả kiểm tra:

{name} (SN: {serial})
Inspecting: {name}

Ví dụ, với đầu vào Smartwatch và 98765:

Smartwatch (SN: 98765)
Inspecting: Smartwatch

Bạn sẽ nhận hai đầu vào: tên gadget và số sê-ri (phân tích thành u32).

Tự làm thử

mod product;

use product::{Gadget, full_report};

fn main() {
    // Đọc các đầu vào
    let mut name = String::new();
    std::io::stdin().read_line(&mut name).expect("Failed to read line");
    let name = name.trim().to_string();
    
    let mut serial_input = String::new();
    std::io::stdin().read_line(&mut serial_input).expect("Failed to read line");
    let serial: u32 = serial_input.trim().parse().expect("Failed to parse serial");
    
    // TODO: Tạo một instance Gadget với name và serial
    
    // TODO: Gọi full_report với instance Gadget của bạn
}
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