Ghi đè cách triển khai mặc định
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 32 trên 61.
Các phần triển khai mặc định rất tiện lợi, nhưng đôi khi một kiểu dữ liệu cần hành vi khác với tiêu chuẩn. Khi đó, bạn có thể ghi đè phần mặc định bằng cách cung cấp phần triển khai của riêng mình trong khối impl.
Chỉ cần định nghĩa phương thức với cùng chữ ký, nhưng thêm logic tùy chỉnh của bạn:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
struct Robot {
id: u32,
}
impl Greet for Robot {
fn greet(&self) -> String {
format!("BEEP BOOP. Unit {} online.", self.id)
}
}
Mặc dù Greet cung cấp phương thức greet mặc định, Robot thay thế hoàn toàn phương thức đó bằng phiên bản riêng. Khi bạn gọi greet() trên một Robot, Rust dùng phần triển khai tùy chỉnh:
let bot = Robot { id: 42 };
println!("{}", bot.greet()); // BÍP BÚP. Đơn vị 42 đã trực tuyến.
Điều này giúp bạn linh hoạt hơn: các kiểu dữ liệu có thể dùng giá trị mặc định thì không cần viết gì, còn các kiểu cần hành vi chuyên biệt có thể ghi đè những phương thức mà chúng cần. Trait vẫn đảm bảo mọi kiểu dữ liệu triển khai trait đều có phương thức này.
Thử thách
DễHãy xây dựng một hệ thống thông báo, trong đó các loại cảnh báo khác nhau có thể tùy chỉnh thông điệp của mình! Bạn sẽ tạo một trait với phần triển khai mặc định, sau đó để một kiểu dùng mặc định, còn kiểu kia ghi đè bằng hành vi riêng.
Bạn sẽ sắp xếp code trong ba tệp:
notifiable.rs: Định nghĩa trait công khaiNotifiablevới phương thứcnotify(&self) -> Stringcó phần triển khai mặc định trả về"Alert: Something happened!".alerts.rs: Tạo hai struct công khai triển khai trait theo những cách khác nhau:GenericAlert: một unit struct dùng thông báo mặc định (khối impl rỗng)UrgentAlert: một struct có trường công khaimessage(String), ghi đè cách triển khai mặc định để trả về"URGENT: {message}", trong đó{message}là thông điệp được lưu trong struct
main.rs: Kết hợp các module và minh họa cả hai hành vi. Tạo mộtGenericAlertvà mộtUrgentAlertbằng đầu vào được cung cấp, sau đó in các thông báo của chúng.
Điểm mấu chốt ở đây là GenericAlert có sẵn hành vi từ cách triển khai mặc định, còn UrgentAlert cung cấp phiên bản chuyên biệt của riêng mình bằng cách định nghĩa phương thức trong khối impl.
Đầu ra của bạn phải theo định dạng sau:
Alert: Something happened!
URGENT: {message}Ví dụ, với đầu vào Server is down!:
Alert: Something happened!
URGENT: Server is down!Bạn sẽ nhận một đầu vào: thông điệp cho cảnh báo khẩn cấp.
Tự làm thử
mod notifiable;
mod alerts;
use alerts::{GenericAlert, UrgentAlert};
use notifiable::Notifiable;
fn main() {
// Đọc đầu vào cho thông báo khẩn cấp
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let message = input.trim().to_string();
// TODO: Tạo một instance GenericAlert
// TODO: Tạo một instance UrgentAlert với thông điệp đầu vào
// TODO: In ra thông báo từ GenericAlert
// TODO: In ra thông báo từ UrgentAlert
}
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 độ3Enum nâng cao
Enum có dữ liệuPhương thức của enumKhớp các biến thể dữ liệuXem lại enum OptionÔn tập: enum Shape6Định nghĩa trait
Trait là gì?Triển khai traitCách triển khai mặc địnhGhi đè cách triển khai mặc địnhTrait có tham sốÔn tập: trình phát đa phương tiệnTự luyện tập: Trình biên dịch Rust online