Trait 约束语法
本课属于 Coddy Rust 学习旅程的“面向对象编程”单元。第 40 课,共 61 课。
到目前为止,你已经学会了定义特征并为结构体实现它们。你还使用泛型编写了灵活的代码。现在该把这些概念结合起来了:使用特征来限制泛型函数可以接受的类型。
考虑一个通用函数,它需要在参数上调用特定方法。如果没有任何约束,Rust 无法保证类型 T 具有该方法:
fn print_info<T>(item: T) {
println!("{}", item.summarize()); // 错误!T 可能没有 summarize() 方法
}
解决办法是使用trait 约束。在泛型参数后添加 : TraitName,即可告诉 Rust,T 必须实现该 trait:
trait Summary {
fn summarize(&self) -> String;
}
fn print_info<T: Summary>(item: T) {
println!("{}", item.summarize()); // 现在 Rust 知道这个方法存在了
}
语法 <T: Summary> 表示“T 是任何实现了 Summary 的类型”。现在,这个函数只接受具有所需行为的类型。如果你尝试传入未实现 Summary 的类型,编译器会拒绝它,并给出明确的错误提示。
这种模式很强大,因为它让你能够编写通用代码,同时仍然可以使用特定功能。你的函数保持灵活,适用于任何类型,但只有提供所需行为的类型才能使用。
挑战
简单让我们构建一个通知系统,使用 trait 约束确保只能发送格式正确的消息!你将创建一个泛型函数,它接受任何实现了特定 trait 的类型,以此展示 trait 约束如何限制泛型参数。
你将把代码分成两个文件:
notification.rs:定义一个公开的Notifytrait,其中包含一个名为message的方法,该方法接受&self并返回一个String。然后创建两个实现了这个 trait 的公开结构体:Email:包含一个公开的subject字段(String)。它的message方法应返回Email: {subject}SMS:包含一个公开的content字段(String)。它的message方法应返回SMS: {content}
send_notification的公开泛型函数,它接受任何实现了Notifytrait 的类型T。这个函数应打印对该项调用message()的结果。main.rs:引入通知模块,并使用提供的输入分别创建一个Email实例和一个SMS实例。分别对它们调用send_notification,以证明你的泛型函数适用于任何实现了Notify的类型。
这里的关键点是,send_notification 不知道它接收的具体类型是什么。它只知道该类型能够生成一条消息。trait 约束 T: Notify 保证了这一能力。
你的输出应显示发送了这两条通知:
Email: {subject}
SMS: {content}例如,输入 Meeting Tomorrow 和 On my way! 时:
Email: Meeting Tomorrow
SMS: On my way!你将收到两个输入:邮件主题和 SMS 内容。
亲手试试
mod notification;
use notification::{Email, SMS, send_notification};
fn main() {
// 读取输入
let mut subject = String::new();
std::io::stdin().read_line(&mut subject).expect("Failed to read line");
let subject = subject.trim().to_string();
let mut content = String::new();
std::io::stdin().read_line(&mut content).expect("Failed to read line");
let content = content.trim().to_string();
// TODO: 使用 subject 创建一个 Email 实例
// TODO: 使用 content 创建一个 SMS 实例
// TODO: 将 email 传给 send_notification 并调用它
// TODO: 将 sms 传给 send_notification 并调用它
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Rust 在线编译器