Cú pháp ràng buộc trait
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 40 trên 61.
Đến giờ, bạn đã học cách định nghĩa trait và triển khai chúng cho các struct. Bạn cũng đã dùng generic để viết code linh hoạt. Bây giờ là lúc kết hợp các khái niệm này: dùng trait để giới hạn những kiểu dữ liệu mà một hàm generic có thể chấp nhận.
Xét một hàm tổng quát cần gọi một phương thức cụ thể trên tham số của nó. Nếu không có ràng buộc nào, Rust không đảm bảo rằng kiểu T có phương thức đó:
fn print_info<T>(item: T) {
println!("{}", item.summarize()); // Lỗi! T có thể không có phương thức summarize()
}
Lời giải là một ràng buộc trait. Khi thêm : TraitName sau tham số tổng quát, bạn cho Rust biết rằng T phải triển khai trait đó:
trait Summary {
fn summarize(&self) -> String;
}
fn print_info<T: Summary>(item: T) {
println!("{}", item.summarize()); // Giờ Rust biết phương thức (method) này tồn tại
}
Cú pháp <T: Summary> có nghĩa là “T là bất kỳ kiểu nào triển khai Summary”. Giờ đây, hàm chỉ chấp nhận những kiểu có hành vi bắt buộc. Nếu bạn thử truyền một kiểu không triển khai Summary, trình biên dịch sẽ từ chối kiểu đó và đưa ra lỗi rõ ràng.
Mẫu này rất hữu ích vì cho phép bạn viết đoạn code dùng chung mà vẫn truy cập được chức năng cụ thể. Hàm (function) của bạn vẫn linh hoạt và hoạt động với mọi kiểu dữ liệu, nhưng chỉ những kiểu dữ liệu cung cấp hành vi bạn cần.
Thử thách
DễHãy xây dựng một hệ thống thông báo dùng giới hạn trait để đảm bảo chỉ có thể gửi những tin nhắn được định dạng đúng! Bạn sẽ tạo một hàm tổng quát chấp nhận mọi kiểu dữ liệu triển khai một trait cụ thể, qua đó minh họa cách giới hạn trait ràng buộc các tham số tổng quát.
Bạn sẽ sắp xếp code trong hai tệp:
notification.rs: Định nghĩa một traitNotifycông khai có phương thứcmessagenhận&selfvà trả về mộtString. Sau đó, tạo hai struct công khai triển khai trait này:Email: có thuộc tínhsubjectcông khai (String). Phương thứcmessagesẽ trả vềEmail: {subject}SMS: có thuộc tínhcontentcông khai (String). Phương thứcmessagesẽ trả vềSMS: {content}
send_notification, chấp nhận mọi kiểu dữ liệuTtriển khai traitNotify. Hàm này sẽ in ra kết quả khi gọimessage()trên đối tượng.main.rs: Đưa module thông báo vào và dùng các đầu vào được cung cấp để tạo một instanceEmailvà một instanceSMS. Gọisend_notificationvới từng instance để chứng minh hàm tổng quát của bạn hoạt động với mọi kiểu dữ liệu triển khaiNotify.
Điểm mấu chốt là send_notification không biết kiểu dữ liệu cụ thể mà nó nhận được. Hàm chỉ biết kiểu dữ liệu đó có thể tạo ra một tin nhắn. Giới hạn trait T: Notify đảm bảo khả năng này.
Đầu ra của bạn sẽ hiển thị cả hai thông báo đã được gửi:
Email: {subject}
SMS: {content}Ví dụ, với đầu vào Meeting Tomorrow và On my way!:
Email: Meeting Tomorrow
SMS: On my way!Bạn sẽ nhận hai đầu vào: tiêu đề email và nội dung SMS.
Tự làm thử
mod notification;
use notification::{Email, SMS, send_notification};
fn main() {
// Đọc dữ liệu đầu vào
let mut subject = String::new();
std::io::stdin().read_line(&mut subject).expect("Failed to read line");
let subject = subject.trim().to_string();
let mut content = String::new();
std::io::stdin().read_line(&mut content).expect("Failed to read line");
let content = content.trim().to_string();
// TODO: Tạo một instance Email với subject
// TODO: Tạo một instance SMS với content
// TODO: Gọi send_notification với email
// TODO: Gọi send_notification với sms
}
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