1. 泛型中的 trait 限定
- 在 Rust 中,泛型
T可以通过trait来约束。 - 比如
fn print_len<T: Display>(x: T),就规定了T必须实现了Display,这样编译器才能保证你在函数里可以安全调用x.to_string()。 👉 这就是 用行为(trait)来限定类型范围。
2. 共同行为的抽象
-
如果有一堆类型,它们虽然本身不同,但都实现了某个 trait,那么我们可以说它们属于同一个“行为集合”。
-
比如:
String&stri32这些都实现了Display,所以它们都属于“能展示的东西”这个集合。
3. trait 对象
- 有时候我们不想提前知道具体的类型,而只想“存放/使用某个实现了某个行为的东西”。
- 这时我们就可以用 trait 对象,语法是
&dyn Trait或Box<dyn Trait>。
例子:
fn print_it(x: &dyn std::fmt::Display) {
println!("{}", x);
}
fn main() {
let s = String::from("hello");
let n = 42;
print_it(&s); // String 实现了 Display
print_it(&n); // i32 也实现了 Display
}
这里 print_it 并不关心传进来的是 String 还是 i32,它只知道参数一定有 Display 这个行为。
这就相当于 面向对象里的“多态”,把一堆不同类型看作“同一类对象”来操作。
4. 为什么叫“对象”
- 在 OOP(面向对象编程)语言里,对象就是“一个有方法的东西”。
- 在 Rust 里,
dyn Trait就是“一个实现了某些方法的抽象引用”。 - 所以叫它 trait 对象,因为它等价于一个“行为被封装成对象”的抽象。
总结:
- 泛型约束:编译期多态(知道具体类型)。
- trait 对象:运行期多态(不知道具体类型,只知道行为)。
就像 Java 里:
- 泛型约束:
<T extends Comparable<T>>。 - trait 对象:把一堆实现了接口
Comparable的对象放到同一个集合里,用接口引用去操作。
Dyn兼容
Dyn 兼容的 trait 可以作为 trait 对象(trait object)的基础。 一个 trait 如果满足以下条件,则是 dyn 兼容的:
- 所有超 trait 也必须是 dyn 兼容的
- 不得要求
Self: Sized - 不得包含任何关联常量
- 不得有带泛型的关联类型
- 所有关联函数必须可以通过 trait 对象调度(dispatchable)或者显式声明不可调度(non-dispatchable)
可调度函数要求:
-
不包含任何类型参数(允许生命周期参数)
-
方法中
Self只能出现在接收者类型中 -
接收者类型必须是下列之一:
&Self、&mut SelfBox<Self>、Rc<Self>、Arc<Self>Pin<P>,其中 P 是以上类型之一
-
返回类型不能是隐藏类型,例如:
async fn(返回Future)impl Trait返回类型
-
函数不能有
where Self: Sized约束
显式不可调度函数要求:
- 函数必须有
where Self: Sized约束(接收者是self时隐含此约束)