Rust-泛型中的 trait 限定

28 阅读2分钟

1. 泛型中的 trait 限定

  • 在 Rust 中,泛型 T 可以通过 trait 来约束。
  • 比如 fn print_len<T: Display>(x: T),就规定了 T 必须实现了 Display,这样编译器才能保证你在函数里可以安全调用 x.to_string()。 👉 这就是 用行为(trait)来限定类型范围

2. 共同行为的抽象

  • 如果有一堆类型,它们虽然本身不同,但都实现了某个 trait,那么我们可以说它们属于同一个“行为集合”。

  • 比如:

    • String
    • &str
    • i32 这些都实现了 Display,所以它们都属于“能展示的东西”这个集合。

3. trait 对象

  • 有时候我们不想提前知道具体的类型,而只想“存放/使用某个实现了某个行为的东西”。
  • 这时我们就可以用 trait 对象,语法是 &dyn TraitBox<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 Self
    • Box<Self>Rc<Self>Arc<Self>
    • Pin<P>,其中 P 是以上类型之一
  • 返回类型不能是隐藏类型,例如:

    • async fn(返回 Future
    • impl Trait 返回类型
  • 函数不能有 where Self: Sized 约束

显式不可调度函数要求

  • 函数必须有 where Self: Sized 约束(接收者是 self 时隐含此约束)