Rust中的traits是一种定义接口的方式:它们描述了一个类型必须实现的方法集合。实现trait允许使用泛型和动态方法替换。
静态调度(通过泛型和约束):
在编译时调用特定方法的实现。使用泛型代码(impl<T: Trait>或fn foo<T: Trait>(t: T)),在编译时为每个类型创建一个单独的函数版本(单态化)。
动态调度:
当函数接受作为trait引用的对象(&dyn Trait)时使用,编译器无法提前知道将使用什么类型。方法调用通过vtable(虚拟表)在运行时进行。
示例:
trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } // 静态调度 def print_animal_static<T: Animal>(a: T) { a.speak(); } // 动态调度 def print_animal_dyn(a: &dyn Animal) { a.speak(); }
何时使用:
是否可以在Vec<Trait>中存储泛型类型的值?为什么?
回答:
不,可以这样做。Vec要求有大小的类型,而traits本身没有大小。必须使用Box<dyn Trait>或trait的引用。
// let v: Vec<Trait> = vec![]; // 编译错误 let v: Vec<Box<dyn Animal>> = vec![Box::new(Dog)]; // 正确
故事
尝试创建trait对象集合而不使用Box或Rc:
// let coll: Vec<MyTrait> = vec![]; // 错误:无法知道...的值的大小
故事
故事
在演示客户中,试图存储对临时对象的引用(&dyn Trait)以与不同协议交互,导致悬空引用和运行时恐慌。不得不使用Box<dyn Trait>进行显式所有权。