Appearance
22.5 闭包与生命周期
当使用闭包做参数或返回值的时候,生命周期会变得更加复杂。假设有下面这段代码:
rust
fn calc_by<'a, F>(var: &'a i32, f: F) -> i32
where F: Fn(&'a i32) -> i32
{
f(var)
}
fn main() {
let local = 10;
let result = calc_by(&local, |i| i*2);
println!("{}", result);
}
这段代码可以编译通过。但是,假如我们把 calc_by
的函数体稍微改一下,变成下面这样:
rust
let local = *var;
f(&local)
就不能编译通过了。
但是,如果我们把所有的生命周期标记去掉,变成这样:
rust
fn calc_by<F>(var: &i32, f: F) -> i32
where F: Fn(&i32) -> i32
{
let local = *var;
f(&local)
}
fn main() {
let local = 10;
let result = calc_by(&local, |i| i*2);
println!("{}", result);
}
这时所有的借用生命周期由编译器自动补全,又可以编译通过了。这说明,我们之前对这个 calc_by
函数手写的生命周期标记有问题。那我们手动来补全这些标记,应该怎么做呢?
在这里我们只能使用“高阶生命周期约束”,即高阶特征约束(Higher-Ranked Trait Bounds)(HRTBs) 的表示方法:
rust
fn calc_by<'a, F>(var: &'a i32, f: F) -> i32
where for<'f> F: Fn(&'f i32) -> i32
{
let local = *var;
f(&local)
}
注意 F
的约束条件,其中 for<'f>
是高阶约束的标志,声明了一个通用生命周期 'f
。这样写表示的意思是,Fn 的输入参数摆脱外部参数生命周期的限制,适配任意合法的生命周期'f
。这个生命周期和另外一个参数 var
的生命周期没有半点关系。
这才是这段代码正确的生命周期标记方式。如果我们不手动标记出来,编译器会巧妙地创建了一些隐式的高阶生命周期参数边界(HRTBs)。
谈到“高阶”这两个字,很多朋友会想到高阶类型(Higher-Kinded Types)。这里的高阶生命周期确实跟高阶类型有很多相似之处,Rust 也确实在思考如何引入高阶类型这个问题。到目前为止,for<'a> Fn(&'a Arg) -> &'a Ret
这样的语法,只能用于生命周期参数,不能用于任意泛型类型。