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.
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 khaiInspectablevới phương thứcinspect, nhận&selfvà trả về mộtStringchứa thông tin kiểm tra. Sau đó, tạo struct công khaiGadgetvới các trường công khainame(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ênfull_report, chấp nhận mọi kiểuTtriển khai cảDisplaylẫnInspectable. 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ọiinspect().main.rs: Đưa module product vào và tạo một instanceGadgetbằng các đầu vào đã cho. Gọifull_reportvớ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: SmartwatchBạ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
}
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
1Phương thức và hành vi
Nhập môn khối triển khaiTham số selfPhương thức có thể thay đổiHàm liên kếtNhiều khối triển khaiNối chuỗi phương thứcÔn tập: thao tác với hình chữ nhật4Dự án: Thú cưng ảo
Định nghĩa thú cưngCho thú cưng ăn7Các trait tiêu chuẩn
Trait DebugTrait DisplayClone và CopyCác trait so sánh bằngÔn tập: Điểm có thể in ra2Tính đóng gói và module
Kiến thức cơ bản về moduleTừ khóa publicTrường riêng tưHàm lấy giá trịHàm thiết lập giá trịÔn tập: tủ khóa an toàn5Kiểu tổng quát
Cấu trúc tổng quátPhương thức tổng quátNhiều kiểu tổng quátHàm tổng quátÔn tập: điểm tọa độ8Trait làm ràng buộc
Cú pháp ràng buộc traitNhiều ràng buộcMệnh đề whereTrả về kiểu có traitÔn tập: hàm in tổng quátTự luyện tập: Trình biên dịch Rust online