Duyệt qua các đối tượng 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 47 trên 61.
Giờ đây, khi đã có thể lưu trữ nhiều kiểu khác nhau trong Vec<Box<dyn Trait>>, bước tiếp theo là xử lý tập hợp đó. Duyệt qua các đối tượng trait cũng giống như duyệt qua bất kỳ vector nào. Bạn lặp qua từng phần tử và gọi các phương thức được định nghĩa trong trait.
trait Shape {
fn area(&self) -> f64;
}
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
impl Shape for Circle {
fn area(&self) -> f64 { 3.14159 * self.radius * self.radius }
}
impl Shape for Rectangle {
fn area(&self) -> f64 { self.width * self.height }
}
fn main() {
let shapes: Vec<Box<dyn Shape>> = vec![
Box::new(Circle { radius: 2.0 }),
Box::new(Rectangle { width: 3.0, height: 4.0 }),
Box::new(Circle { radius: 1.0 }),
];
for shape in &shapes {
println!("Area: {}", shape.area());
}
}
Khi lặp qua for shape in &shapes, mỗi shape là một tham chiếu đến Box<dyn Shape>. Bạn có thể gọi trực tiếp bất kỳ phương thức nào từ trait Shape: Rust tự động xử lý việc điều phối động và gọi phần triển khai phù hợp cho từng kiểu cụ thể.
Mẫu này rất hữu ích để tổng hợp kết quả. Bạn có thể tính tổng diện tích, lọc hình theo kích thước hoặc biến đổi tập hợp. Điểm mấu chốt là khi các phần tử đã nằm trong một tập hợp đối tượng trait, bạn chỉ tương tác với chúng thông qua giao diện của trait, bất kể kiểu nền tảng của chúng là gì.
Thử thách
DễHãy tạo một trình tính giá cho giỏ hàng có thể chứa nhiều loại mặt hàng! Bạn sẽ xây dựng một hệ thống có thể lưu trữ chung các sản phẩm và dịch vụ, mỗi loại có cách tính giá riêng, rồi xử lý chúng để tính tổng giá.
Bạn sẽ sắp xếp code trong hai tệp:
items.rs: Định nghĩa một trait công khaiPriceablevới phương thứcprice, nhận&selfvà trả về một giá trịf64. Sau đó tạo hai struct công khai:Product: có các trường công khainame(String) vàcost(f64). Phương thứcpricechỉ cần trả vềcost.Service: có các trường công khainame(String),hourly_rate(f64) vàhours(f64). Phương thứcpricecần trả vềhourly_rate * hours.
main.rs: Khai báo module items và tạo một vector kiểuVec<Box<dyn Priceable>>. Dùng đầu vào để tạo một Product và một Service, thêm chúng vào vector, sau đó duyệt qua tập hợp để tính và in ra tổng giá của tất cả mặt hàng.
Trait object thể hiện sức mạnh ở đây. Code duyệt của bạn không cần biết nó đang xử lý Product hay Service. Code chỉ cần gọi price() trên từng mặt hàng, Rust sẽ tự động gọi đúng phần triển khai.
Đầu ra của bạn cần hiển thị tổng giá với hai chữ số thập phân:
Total: ${total}Ví dụ, với đầu vào Laptop, 999.99, Consulting, 150.0 và 3.0:
Total: $1449.99Với đầu vào Book, 29.99, Tutoring, 50.0 và 2.0:
Total: $129.99Bạn sẽ nhận năm đầu vào: tên sản phẩm, giá sản phẩm, tên dịch vụ, giá theo giờ và số giờ làm việc (phân tích các giá trị số thành kiểu f64).
Tự làm thử
mod items;
use items::{Priceable, Product, Service};
fn main() {
// Đọc dữ liệu đầu vào
let mut product_name = String::new();
std::io::stdin().read_line(&mut product_name).expect("Failed to read line");
let product_name = product_name.trim().to_string();
let mut product_cost = String::new();
std::io::stdin().read_line(&mut product_cost).expect("Failed to read line");
let product_cost: f64 = product_cost.trim().parse().expect("Failed to parse");
let mut service_name = String::new();
std::io::stdin().read_line(&mut service_name).expect("Failed to read line");
let service_name = service_name.trim().to_string();
let mut hourly_rate = String::new();
std::io::stdin().read_line(&mut hourly_rate).expect("Failed to read line");
let hourly_rate: f64 = hourly_rate.trim().parse().expect("Failed to parse");
let mut hours = String::new();
std::io::stdin().read_line(&mut hours).expect("Failed to read line");
let hours: f64 = hours.trim().parse().expect("Failed to parse");
// TODO: Tạo Product bằng product_name và product_cost
// TODO: Tạo Service bằng service_name, hourly_rate và hours
// TODO: Tạo Vec<Box<dyn Priceable>> và thêm cả hai mục vào đó
// TODO: Duyệt qua vectơ và tính tổng giá
// TODO: In ra tổng giá với một chữ số sau dấu thập phân
// Định dạng: Total: ${total:.1}
}
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