面试题答案
一键面试// 定义一个trait
trait MyTrait {
fn perform_action(&self) -> String;
}
// 定义第一个结构体并实现MyTrait
struct StructA {
data: String,
}
impl MyTrait for StructA {
fn perform_action(&self) -> String {
format!("StructA: {}", self.data)
}
}
// 定义第二个结构体并实现MyTrait
struct StructB {
value: i32,
}
impl MyTrait for StructB {
fn perform_action(&self) -> String {
format!("StructB: {}", self.value)
}
}
// 定义泛型函数,接受trait对象的Vec作为参数,并返回结果的Vec
fn process_objects<'a>(objects: Vec<&'a dyn MyTrait>) -> Vec<String> {
let mut results = Vec::new();
for object in objects {
let result = object.perform_action();
results.push(result);
}
results
}
泛型约束
- 在定义
process_objects
函数时,使用了泛型生命周期参数'a
。这个参数表示objects
中所有trait对象的生命周期,通过&'a dyn MyTrait
明确表明了这些trait对象的引用必须至少和函数调用所在的作用域一样长。 - 这样的泛型约束确保了在函数内部使用这些trait对象时,它们的生命周期是有效的,避免了悬垂引用等问题。
Trait对象
&'a dyn MyTrait
就是一个trait对象。这里dyn
关键字表明这是一个动态分发的trait对象,即具体的类型在运行时确定。objects
参数是一个Vec<&'a dyn MyTrait>
,意味着它可以接受任何实现了MyTrait
的结构体的引用,只要这些引用的生命周期是'a
。
生命周期
- 生命周期参数
'a
明确了objects
中所有引用的生命周期必须与函数调用所在的作用域一致。这确保了在函数内部访问这些trait对象时,它们仍然有效。 - 在函数内部,
objects
中的每个trait对象的生命周期都被限制在'a
,因此在遍历和调用trait方法时不会出现生命周期相关的错误。
使用示例
fn main() {
let a = StructA { data: "Hello".to_string() };
let b = StructB { value: 42 };
let objects: Vec<&dyn MyTrait> = vec![&a, &b];
let results = process_objects(objects);
for result in results {
println!("{}", result);
}
}
在main
函数中,创建了StructA
和StructB
的实例,将它们的引用收集到Vec
中,并调用process_objects
函数进行处理,最后输出结果。这样就展示了如何在Rust中精确地定义和使用泛型约束、trait对象以及生命周期来实现预期的功能并避免编译器错误。