// 定义泛型结构体
struct GenericStruct<T> {
value: T,
}
// 定义特征
trait GenericTrait {
fn operate(&self);
}
// 为泛型结构体实现特征
impl<T> GenericTrait for GenericStruct<T>
where
T: std::fmt::Display,
{
fn operate(&self) {
println!("The value is: {}", self.value);
}
}
fn main() {
// 创建不同类型实例的泛型结构体
let int_struct = GenericStruct { value: 42 };
let string_struct = GenericStruct { value: "Hello, Rust".to_string() };
// 使用特征对象来存储和调用特征方法
let mut objects: Vec<Box<dyn GenericTrait>> = Vec::new();
objects.push(Box::new(int_struct));
objects.push(Box::new(string_struct));
for obj in &mut objects {
obj.operate();
}
}
生命周期
- 结构体和特征方法中的生命周期:在上述代码中,虽然没有显式指定生命周期参数,Rust 会根据默认规则推断。例如,
operate
方法中的 &self
表示该方法借用 self
,并且这个借用的生命周期与方法调用的生命周期相关联。这确保了在方法执行期间,self
所指向的数据是有效的。
- 特征对象的生命周期:当将泛型结构体实例装箱成特征对象
Box<dyn GenericTrait>
时,特征对象的生命周期取决于其存储的上下文。在 main
函数中,objects
向量中的特征对象的生命周期与 objects
向量本身的生命周期一致。当 objects
超出作用域时,其中的特征对象及其包含的数据也会被销毁。
类型擦除
- 概念:类型擦除指的是在使用特征对象时,编译器不再关心具体的类型,只关心对象实现的特征。在代码中,
Box<dyn GenericTrait>
就是一个特征对象,它可以存储任何实现了 GenericTrait
特征的类型,而不需要知道具体的类型 T
。
- 作用:这使得代码更加灵活和通用。例如,
objects
向量可以存储不同类型的 GenericStruct
实例(int_struct
和 string_struct
),因为它们都实现了 GenericTrait
。在调用 operate
方法时,编译器通过虚表(vtable)来动态调度正确的方法实现,而不需要知道对象的具体类型,从而实现了类型擦除。