Rust泛型Command模式中Sum trait生命周期问题及疑问
泛型Command模式中的生命周期问题
我用Command模式实现功能,最初的非泛型版本可以正常运行。为了练习泛型,我把PrintSum的params改成Vec<T>,并指定T实现Debug + Sum<&'a T>,但出现了生命周期相关的编译错误。后来把params改成&'a Vec<T>后编译通过了,现在有两个疑问:
- 这个改动为什么能解决问题?
- 如何在不把
params改成引用的前提下解决这个问题?
初始可运行代码
trait Command { fn execute(&self); } struct PrintSum { params: Vec<i32> } impl Command for PrintSum { fn execute(&self) { println!("{:?}", self.params.iter().sum::<i32>()); } } fn main() { let command = PrintSum { params: vec![1, 2, 3, 4] }; command.execute() }
泛型报错代码
use std::fmt::Debug; use std::iter::Sum; trait Command { fn execute(&self); } struct PrintSum<T> { params: Vec<T>, } impl<'a, T: 'a> Command for PrintSum<T> where T: Sum<&'a T> + Debug, { fn execute(&self) { println!("{:?}", self.params.iter().sum::<T>()); } } fn main() { let command = PrintSum { params: vec![1, 2, 3], }; command.execute() }
编译错误信息
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements --> src/main.rs:17:38 | 17 | println!("{:?}", self.params.iter().sum::<T>()); | ^^^^ | note: first, the lifetime cannot outlive the anonymous lifetime defined here... --> src/main.rs:16:16 | 16 | fn execute(&self) { | ^^^^^ note: ...so that reference does not outlive borrowed content --> src/main.rs:17:26 | 17 | println!("{:?}", self.params.iter().sum::<T>()); | ^^^^^^^^^^^ note: but, the lifetime must be valid for the lifetime `'a` as defined here... --> src/main.rs:12:6 | 12 | impl<'a, T: 'a> Command for PrintSum<T> | ^^ note: ...so that the types are compatible --> src/main.rs:17:45 | 17 | println!("{:?}", self.params.iter().sum::<T>()); | ^^^ = note: expected `Sum<&T>` found `Sum<&'a T>`
修改后可运行代码
use std::fmt::Debug; use std::iter::Sum; trait Command { fn execute(&self); } struct PrintSum<'a, T> { params: &'a Vec<T>, } impl<'a, T: 'a> Command for PrintSum<'a, T> where T: Sum<&'a T> + Debug, { fn execute(&self) { println!("{:?}", self.params.iter().sum::<T>()); } } fn main() { let command = PrintSum { params: &vec![1, 2, 3], }; command.execute() }
问题解答
1. 为什么改成&'a Vec<T>能解决问题?
当params是&'a Vec<T>时,self.params.iter()生成的元素引用生命周期是'a——因为引用的生命周期继承自被引用的Vec<T>的生命周期。这正好匹配了where约束中Sum<&'a T>的要求,编译器能明确确认引用生命周期和约束一致,不会再出现冲突。
而原版本中,params是Vec<T>,self.params.iter()生成的引用生命周期是execute方法的匿名生命周期(即&self的生命周期),但约束要求的是Sum<&'a T>。匿名生命周期仅在execute调用期间有效,'a是impl块定义的更长生命周期,编译器无法证明二者兼容,因此报错。
2. 不改成引用的前提下如何解决?
可以使用高阶生命周期约束,让T对任意生命周期的引用都满足Sum约束:
use std::fmt::Debug; use std::iter::Sum; trait Command { fn execute(&self); } struct PrintSum<T> { params: Vec<T>, } impl<T> Command for PrintSum<T> where for<'a> T: Sum<&'a T> + Debug, { fn execute(&self) { println!("{:?}", self.params.iter().sum::<T>()); } } fn main() { let command = PrintSum { params: vec![1, 2, 3], }; command.execute() }
for<'a> T: Sum<&'a T>表示:对于任意生命周期'a,T都能实现Sum<&'a T>。这样不管execute中iter()生成的引用生命周期是什么(即那个匿名生命周期),T都满足对应的Sum约束,编译器可以正常推导,不会再出现生命周期冲突。
内容的提问来源于stack exchange,提问作者Miokloń
相关产品推荐
相关产品推荐

