Rust trait 与泛型约束
更新时间:2026-09-18。trait 是 Rust 的「接口 + 能力约束」,配合泛型可以做到零成本抽象。生命周期见 生命周期标注与省略,并发约束见 Send/Sync。
一、定义与实现
rust
trait Summary {
fn author(&self) -> &str;
fn summary(&self) -> String { // 默认方法
format!("by {}", self.author())
}
}
struct Post { name: String }
impl Summary for Post {
fn author(&self) -> &str { &self.name }
}trait 方法只能用在「该 trait 在作用域内」时调用;孤儿规则保证不为外来类型实现外来 trait(至少一方是本地类型)。
二、泛型 + trait bound:静态分发(单态化)
rust
// 两种等价写法
fn notify1<T: Summary>(item: &T) { println!("{}", item.summary()); }
fn notify2(item: &impl Summary) { println!("{}", item.summary()); }
// 多约束与 where
fn use_it<T>(t: T) where T: Summary + Clone + 'static { /*...*/ }编译器为每个具体类型生成一份专门代码(单态化 monomorphization),调用没有虚函数开销,但会增加编译体积/时间。
三、dyn trait:动态分发
当类型集合运行时才确定(如装在一个集合里的不同类型),用 trait object:
rust
let v: Vec<Box<dyn Summary>> = vec![Box::new(Post { name: "a".into() })];- 通过 vtable 间接调用,有一次指针跳转开销,且大小不定要放在
Box/&后。 - 要求 trait 对象安全:方法不能有泛型参数、
Self不能按值出现在参数/返回等,否则不能dyn。
四、关联类型 vs 泛型
rust
trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; } // 关联类型:一种实现对应一个 Item
trait Container<T> {} // 泛型 trait:同一类型可为不同 T 多次实现「一个实现只对应一个关联类型」用 type Item;「同一类型可有多个版本」用泛型参数。
五、常见坑
- 混淆静态/动态分发:要零成本、类型编译期已知用泛型;要混合多种类型才用
dyn,别为了少写泛型到处dyn损失性能。 - trait 未导入导致方法找不到:调用
Iterator等方法要use对应 trait。 - 对象安全报错:
fn method<U>()或返回Self的 trait 不能做成dyn,需要拆分 trait。 - 约束写太死/太松:函数只需要读就用
&T、需要的能力才加进 bound(如T: AsRef<str>比直接收String更通用)。 impl Trait只能返回单一具体类型:要返回不同类型需Box<dyn Trait>。
总结
Rust trait 定义共享行为并可带默认方法,泛型加 trait bound 通过单态化做静态分发、为每个具体类型生成专用代码从而零虚调用开销(impl Trait 是语法糖),类型运行时才确定时才用 Box/&dyn Trait 走 vtable 动态分发并要求对象安全;关联类型表达「一种实现对应一个类型」、泛型 trait 表达「可对多个类型实现」,实战中默认用泛型求性能、用 dyn 求异构集合,并记得调用 trait 方法前先 use、按实际需要给最小约束、impl Trait 返回不了多个具体类型。