Menu
CoddyTech

遍历 Trait 对象

本课属于 Coddy Rust 学习旅程的“面向对象编程”单元。第 47 课,共 61 课。

现在你可以在 Vec<Box<dyn Trait>> 中存储不同类型了,下一步是处理这个集合。遍历 trait 对象的方式和遍历任何向量一样。你可以循环访问每个元素,并调用 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());
    }
}

使用 for shape in &shapes 进行迭代时,每个 shape 都是对 Box<dyn Shape> 的引用。你可以直接调用 Shape trait 中的任何方法:Rust 会自动处理动态分派,并为每种具体类型调用正确的实现。

这种模式非常适合聚合结果。你可以对所有面积求和,按大小筛选形状,或转换整个集合。关键在于,一旦项目被放入特征对象集合中,无论它们底层的类型是什么,你都可以只通过特征的接口与它们交互。

challenge icon

挑战

简单

让我们为购物车构建一个价格计算器,让购物车可以容纳不同类型的商品!你将创建一个系统,把定价逻辑各不相同的商品和服务存放在一起,并进行处理来计算总价。

你将把代码分成两个文件:

  • items.rs:定义一个公开的 Priceable trait,其中包含一个 price 方法,该方法接受 &self 并返回一个 f64。然后创建两个公开的结构体:
    • Product:包含公开的 name(String)和 cost(f64)字段。它的 price 应直接返回 cost。
    • Service:包含公开的 name(String)、hourly_rate(f64)和 hours(f64)字段。它的 price 应返回 hourly_rate * hours。
  • main.rs:引入你的 items 模块,并创建一个类型为 Vec<Box<dyn Priceable>> 的向量。使用输入创建一个 Product 和一个 Service,将它们添加到向量中,然后遍历这个集合,计算并输出所有项目的总价。

trait 对象的优势在这里大放异彩。你的遍历代码不需要知道当前处理的是 Product 还是 Service。它只需对每个项目调用 price(),Rust 就会自动分派到正确的实现。

输出的总价应保留两位小数:

Total: ${total}

例如,输入 Laptop、999.99、Consulting、150.0 和 3.0 时:

Total: $1449.99

输入 Book、29.99、Tutoring、50.0 和 2.0 时:

Total: $129.99

你将收到五个输入:商品名称、商品成本、服务名称、每小时费率和工作小时数(将数值解析为 f64)。

亲手试试

mod items;

use items::{Priceable, Product, Service};

fn main() {
    // 读取输入
    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:使用 product_name 和 product_cost 创建一个 Product

    // TODO:使用 service_name、hourly_rate 和 hours 创建一个 Service

    // TODO:创建一个 Vec<Box<dyn Priceable>>,并将这两个对象添加进去

    // TODO:遍历这个向量,计算总价

    // TODO:输出总价,保留一位小数
    // 格式:Total: ${total:.1}
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Rust 在线编译器