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 函数手写的生命周期标记有问题。
对于上面这个例子,所有的借用生命周期都是由编译器自动补全的,假如我们手动来补全这些标记,应该怎么做呢?
在这里我们只能使用“高阶生命周期”的表示方法:
rust
fn calc_by<'a, F>(var: &'a i32, f: F) -> i32
where F: for<'f> Fn(&'f i32) -> i32
{
let local = *var;
f(&local)
}
注意 F 的约束条件,这样写表示的意思是,Fn 的输入参数可作用于任意的生命周期'f,这个生命周期和另外一个参数 var 的生命周期没有半点关系。
这才是这段代码正确的生命周期标记方式。如果我们不手动标记出来,编译器为我们自动推导的生命周期关系就是这样的高阶生命周期。
谈到“高阶”这两个字,很多朋友会想到高阶类型(higher kinded types)。这里的高阶生命周期确实跟高阶类型有很多相似之处,Rust 也确实在思考如何引入高阶类型这个问题,但还没有做出最终决定。到目前为止,for<'a>Fn(&'a Arg)->&'a Ret 这样的语法,只能用于生命周期参数,不能用于任意泛型类型。