Skip to content

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 这样的语法,只能用于生命周期参数,不能用于任意泛型类型。

Released under the MIT License