Điều phối động
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 45 trên 61.
Khi bạn dùng generic với các ràng buộc trait như <T: Summary>, Rust xác định kiểu chính xác tại thời điểm biên dịch. Cách này gọi là điều phối tĩnh (static dispatch): trình biên dịch tạo mã chuyên biệt cho từng kiểu cụ thể mà bạn dùng. Cách này nhanh, nhưng có một hạn chế: một biến chỉ có thể chứa một kiểu cụ thể.
Nếu bạn cần một biến có thể chứa các kiểu dữ liệu khác nhau trong khi chạy thì sao? Đây là lúc đối tượng trait phát huy tác dụng. Với Box<dyn Trait>, bạn có thể lưu bất kỳ kiểu dữ liệu nào triển khai trait:
trait Speak {
fn speak(&self) -> String;
}
struct Dog;
struct Cat;
impl Speak for Dog {
fn speak(&self) -> String { String::from("Woof!") }
}
impl Speak for Cat {
fn speak(&self) -> String { String::from("Meow!") }
}
fn main() {
let animal: Box<dyn Speak> = Box::new(Dog);
println!("{}", animal.speak()); // "Woof!"
let animal: Box<dyn Speak> = Box::new(Cat);
println!("{}", animal.speak()); // "Meow!"
}
Từ khóa dyn cho biết cơ chế điều phối động (dynamic dispatch): Rust tìm phương thức cần gọi khi chạy chương trình thay vì lúc biên dịch. Box là cần thiết vì các đối tượng trait không có kích thước xác định; Box cung cấp một con trỏ có kích thước cố định.
Hãy nghĩ theo cách này: kiểu tổng quát (generics) nói rằng “mình làm việc với kiểu T”, còn đối tượng trait nói rằng “mình làm việc với bất cứ thứ gì có thể làm việc này”. Đổi lại, bạn phải chấp nhận một chi phí nhỏ khi chạy chương trình để linh hoạt xử lý các kiểu khác nhau thông qua cùng một biến.
Thử thách
DễHãy xây dựng một hệ thống cho thuê phương tiện để thấy sức mạnh của cơ chế điều phối động! Bạn sẽ tạo một trait (đặc tính) định nghĩa những gì mọi phương tiện cho thuê có thể làm, rồi dùng Box<dyn Trait> để lưu các kiểu phương tiện khác nhau trong cùng một biến.
Bạn sẽ sắp xếp code trong hai tệp:
vehicle.rs: Định nghĩa traitRentablecông khai, có phương thứcrental_infonhận&selfvà trả về mộtString. Sau đó, tạo hai struct công khai:Car: có trườngmodelcông khai (String). Phương thứcrental_infosẽ trả vềCar: {model}Bike: có trườngbrandcông khai (String). Phương thứcrental_infosẽ trả vềBike: {brand}
main.rs: Đưa module phương tiện vào và dùng các đầu vào để minh họa cơ chế điều phối động. Tạo một biếnBox<dyn Rentable>ban đầu lưu mộtCar, in thông tin cho thuê của nó, sau đó gán lại cùng biến đó để lưu mộtBikevà in thông tin cho thuê của nó lần nữa.
Điều thú vị ở đây là một biến duy nhất có kiểu Box<dyn Rentable> có thể lưu một Car hoặc một Bike: Rust sẽ xác định cần gọi phương thức rental_info nào khi chương trình chạy. Đây chính là cơ chế điều phối động!
Đầu ra của bạn sẽ hiển thị thông tin của cả hai phương tiện:
Car: {model}
Bike: {brand}Ví dụ, với đầu vào Tesla Model 3 và Trek:
Car: Tesla Model 3
Bike: TrekBạn sẽ nhận hai đầu vào: mẫu xe hơi và thương hiệu xe đạp.
Tự làm thử
mod vehicle;
use vehicle::{Rentable, Car, Bike};
fn main() {
// Đọc đầu vào
let mut car_model = String::new();
std::io::stdin().read_line(&mut car_model).expect("Failed to read line");
let car_model = car_model.trim().to_string();
let mut bike_brand = String::new();
std::io::stdin().read_line(&mut bike_brand).expect("Failed to read line");
let bike_brand = bike_brand.trim().to_string();
// TODO: Tạo biến kiểu Box<dyn Rentable> để chứa một Car
// In ra thông tin cho thuê của Car bằng phương thức rental_info()
// TODO: Gán lại cho biến đó một Bike
// In ra thông tin cho thuê của Bike lần nữa
}
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 độTự luyện tập: Trình biên dịch Rust online