为什么不能从闭包中返回引用?

5
fn main() {
    let _ref_in_ref_out = |var: &i64| var;
}

以下代码无法编译:

error: lifetime may not live long enough
 --> src/main.rs:2:39
  |
2 |     let _ref_in_ref_out = |var: &i64| var;
  |                                 -   - ^^^ returning this value requires that `'1` must outlive `'2`
  |                                 |   |
  |                                 |   return type of closure is &'2 i64
  |                                 let's call the lifetime of this reference `'1`

显然,编译器推断出两个不同的生命周期(分别适用于参数和返回类型),而不是相同的生命周期。

是否有可能编写一个闭包,使输入生命周期与输出生命周期相同?

例如:

fn ref_in_ref_out<'a> (var: &'a i64) -> &'a i64  { var }

但是作为一个闭包

2个回答

10

生命周期省略规则不适用于闭包,你也不能显式地为闭包指定生命周期。但是有几种方法可以使这个代码工作。

最简单的解决方法是简单地省略类型注释,让编译器推断一切:

let ref_in_ref_out = |var| var;
let i: i64 = 42;
ref_in_ref_out(&i);

或者,指定返回类型也完全可以。这会编译通过:

let _ref_in_ref_out = |var| -> &i64 { var };

如果您的闭包没有封闭任何局部变量的话,另一个选项是将其转换为函数指针,因为生命周期省略规则适用于函数指针:

let ref_in_ref_out: fn(&i64) -> &i64 = |var| var;

最后,最常见的解决方法是使用一个帮助函数来应用函数特质绑定到你的闭包中:

fn constrain_closure<F: Fn(&i64) -> &i64>(f: F) -> F {
    f
}

let _ref_in_ref_out = constrain_closure(|var| var);

1

我刚刚找到了解决这个问题的方法,但如果有更简单的解决方案就太好了。

fn infer_lifetime<'a, T: 'a, F: Fn(&'a T) -> &'a T>(f: F) -> F {
    f
}

fn main() {
    let _ref_in_ref_out = infer_lifetime(|var: &i64| var);
}

2
请注意,即使没有显式的生命周期参数,这段代码也可以编译通过,但是你会得到更一般的限制for<'a> Fn(&'a i64) -> &'a i64。本答案中的代码生成一个适用于特定推断生命周期的闭包。依赖于生命周期省略会导致闭包适用于任何生命周期'a,因此该闭包可以使用不同的生命周期进行调用。 - Sven Marnach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接