多个约束
本课属于 Coddy Rust 学习旅程的“面向对象编程”单元。第 41 课,共 61 课。
有时,一个 trait 约束还不够。你可能需要一个既能打印又能提供摘要的泛型类型。Rust 允许你使用 + 语法来要求多个 trait。
下面介绍如何指定某个类型必须实现两个 trait:
use std::fmt::Display;
trait Summary {
fn summarize(&self) -> String;
}
fn announce<T: Display + Summary>(item: T) {
println!("Breaking news: {}", item);
println!("Summary: {}", item.summarize());
}
这个约束 T: Display + Summary 表示“T 必须实现 Display 和 Summary”。在函数内部,你可以使用这两个 trait 的功能:用 {} 打印(来自 Display),以及调用 summarize()(来自 Summary)。
你可以根据需要链接任意多个特征:
fn process<T: Display + Summary + Clone>(item: T) {
// 可以打印、生成摘要,还可以克隆
}
当你的函数依赖多种行为时,这种模式至关重要。与其接受任意类型并寄希望于它能正常工作,不如明确声明所需的具体功能,并让编译器在编译时强制检查。
挑战
简单让我们构建一个产品检查系统,要求物品具备多种能力!你将创建一个泛型函数,它只接受同时实现自定义 trait 和标准 trait 的类型,以此展示 + 语法如何组合多个约束。
你将把代码分成两个文件:
product.rs:定义一个公开的Inspectabletrait,其中包含一个名为inspect的方法。该方法接收&self,并返回包含检查详情的String。然后创建一个公开的Gadget结构体,其中包含公开的name(String)和serial(u32)字段。你的 Gadget 应实现Inspectable(返回Inspecting: {name})和std::fmt::Display(格式为{name} (SN: {serial}))。最后,创建一个名为full_report的公开泛型函数,它接受任何同时实现了Display和Inspectable的类型T。此函数应输出两行:第一行使用{}格式化器输出该物品,第二行输出调用inspect()的结果。main.rs:引入你的 product 模块,并使用提供的输入创建一个Gadget实例。使用你的 gadget 调用full_report,以展示它满足这两个 trait 的要求。
多个约束的优势在于,你的 full_report 函数可以使用这两个 trait 提供的能力,既能清晰地显示物品,又能获取检查详情,并且这些要求都能在编译时得到保证。
你的输出应同时显示格式化后的物品和检查结果:
{name} (SN: {serial})
Inspecting: {name}例如,输入 Smartwatch 和 98765 时:
Smartwatch (SN: 98765)
Inspecting: Smartwatch你将收到两个输入:设备名称和序列号(解析为 u32)。
亲手试试
mod product;
use product::{Gadget, full_report};
fn main() {
// 读取输入
let mut name = String::new();
std::io::stdin().read_line(&mut name).expect("Failed to read line");
let name = name.trim().to_string();
let mut serial_input = String::new();
std::io::stdin().read_line(&mut serial_input).expect("Failed to read line");
let serial: u32 = serial_input.trim().parse().expect("Failed to parse serial");
// TODO:用名称和序列号创建一个 Gadget 实例
// TODO:用你创建的 Gadget 实例调用 full_report
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Rust 在线编译器