简介
有些互不相关的数据类型需要遵循相同的行为约定。例如,温室传感器和货物运输记录包含不同的字段,但都可以描述自身的当前状态。Rust Trait 可以为这种共享能力命名,而不要求这些类型采用相同的数据布局。
在本实验中,你将定义一个小型 Trait,为两个预先准备好的结构体实现该 Trait,并将两个值传递给同一个泛型报告函数。较长的结构体和示例数据已经准备好,因此你只需专注于每处编辑。
定义共享的状态契约
在这一步中,你将定义一个 Trait,为其他方面互不相关的类型声明一项共同的行为。
进入预先准备好的软件包,并查看完整的代码框架。sed -n 命令只会打印指定行,不会修改文件:
cd /home/labex/project/status-reporter
sed -n '1,160p' src/main.rs
Sensor 和 Shipment 包含不同的字段。它们预先准备好的文本字段使用 &'static str,表示借用的字符串字面量,并且在整个程序运行期间都保持有效。'static 生命周期标注不属于本实验的 Trait 主题,这里会暂时跳过;你不需要修改或分析它。
在占位符下方,show_status 已经是一个泛型函数。将 T: Status 分成三部分阅读:T 表示某个具体类型,冒号表示「必须满足」,Status 表示所需的 Trait。用普通语言来说,就是「对于任意类型 T,只要 T 实现了 Status」。有了这个约定,函数就可以安全地调用 item.status()。
Rust 必须先认识这个 Trait,才能理解该 Trait Bound。打开源文件:
nano src/main.rs
将 // STATUS_TRAIT 替换为:
trait Status {
fn status(&self) -> String;
}
方法声明末尾使用分号,是因为 Trait 只声明了一个必需的方法,并没有指定具体实现。&self 允许方法检查一个值而不取得其所有权,String 则允许每种类型构造自己的状态文本。之后的每个实现都必须完全重复这个方法名、接收者和返回类型,然后提供自己的方法体。
按 Ctrl+O 保存,按 Enter 确认,然后按 Ctrl+X 退出。接着检查软件包:
cargo check
此时可能会看到 Trait 和预先准备好的项目尚未使用的警告。最后出现 Finished 行就是重要的成功依据,表示共享契约和泛型 Trait Bound 有效。接下来的两步会添加具体实现和函数调用。
为 Sensor 实现 Status
在这一步中,你将为 Sensor 履行 Trait 契约,并将一个传感器传递给共享报告函数。
impl Trait for Type 代码块会将已有的 Trait 连接到某个具体类型。该代码块中的方法签名必须与 Trait 匹配,但方法体可以使用该类型自身的字段。
打开源文件:
nano src/main.rs
将 // SENSOR_IMPLEMENTATION 替换为以下简短实现:
impl Status for Sensor {
fn status(&self) -> String {
format!("{}: {}°C", self.location, self.celsius)
}
}
format! 会构造并返回一个 String,不会自行打印内容。由于 self 是借用的 Sensor,该实现可以使用 location 和 celsius 字段。
接下来,将 main 中的 // SENSOR_REPORT 替换为:
show_status("Sensor", &sensor);
&sensor 参数与报告函数的借用参数 &T 相匹配,因此 sensor 的所有权仍由 main 保留。保存并退出 Nano,然后运行程序:
cargo run --quiet
Sensor: Greenhouse: 24°C
这行输出证明 Sensor 满足 Status,并且可以通过带 Trait Bound 的泛型报告函数。此时,货物运输记录还没有连接到该函数。
为 Shipment 实现 Status
在这一步中,你将为 Shipment 实现同一个 Trait,并证明一个报告函数可以接收这两种具体类型。
再次打开源文件:
nano src/main.rs
将 // SHIPMENT_IMPLEMENTATION 替换为:
impl Status for Shipment {
fn status(&self) -> String {
format!("#{} {}", self.id, self.stage)
}
}
这个方法遵循相同的 Trait 契约,但使用货物运输记录自身的 id 和 stage 字段。接下来,将 // SHIPMENT_REPORT 替换为:
show_status("Shipment", &shipment);
保存并退出 Nano。运行完成后的软件包:
cargo run --quiet
Sensor: Greenhouse: 24°C
Shipment: #204 in transit
这两行输出都来自 show_status<T: Status>。Trait Bound 描述函数所需的行为,而每个实现则决定自身的值如何提供这种行为。
总结
你使用 Trait 定义了共享行为,分别为两个结构体实现了该行为,并使用基本的 Trait Bound,让同一个泛型报告函数可以接收这两种类型。


