Menu
CoddyTech

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.

challenge icon

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 khai Notifiable với phương thức notify(&self) -> String có 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 khai message (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ột GenericAlert và một UrgentAlert bằ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
}
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