Rust 函数返回一个闭包:“需要明确的生命周期绑定"; [英] Rust function returning a closure: ``explicit lifetime bound required"

查看:70
本文介绍了Rust 函数返回一个闭包:“需要明确的生命周期绑定";的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码无法编译.

fn main() {
    let foo = bar(8);

    println!("Trying `foo` with 4: {:d}", foo(4));
    println!("Trying `foo` with 8: {:d}", foo(8));
    println!("Trying `foo` with 13: {:d}", foo(13));
}

//

fn bar(x: int) -> (|int| -> int) {
    |n: int| -> int {
        if n < x { return n }

        x
    }
}

错误如下.

11:32 error: explicit lifetime bound required
.../hello/src/main.rs:11 fn bar(x: int) -> (|int| -> int) {
                                            ^~~~~~~~~~~~

我将整数参数按值传递给 bar.为什么 Rust 关心按值传递的整数的生命周期?编写这样一个返回闭包的函数的正确方法是什么?谢谢.

I am passing the integer argument to bar by value. Why does Rust care about the lifetime of an integer passed by value? What is the correct way of writing such a function that returns a closure? Thanks.

编辑

我在手册中发现了以下内容.在最简单和最便宜的形式(类似于 || { } 表达式)中,lambda 表达式通过引用捕获其环境,有效地借用指向函数内部提到的所有外部变量的指针.或者,编译器可能会推断 lambda 表达式应该将值(取决于它们的类型)从环境复制或移动到 lambda 表达式的捕获环境中.

I found the following in the manual. In the simplest and least-expensive form (analogous to a || { } expression), the lambda expression captures its environment by reference, effectively borrowing pointers to all outer variables mentioned inside the function. Alternately, the compiler may infer that a lambda expression should copy or move values (depending on their type.) from the environment into the lambda expression's captured environment.

是否有进一步说明编译器如何推断是通过引用捕获外部变量、复制它们还是移动它们?评估标准是什么,它们的应用顺序是什么?这是否记录在案(没有阅读编译器的代码)?

Is there a further specification of how the compiler infers whether to capture the outer variables by reference, copy them or move them? What are the evaluation criteria, and what is their order of application? Is this documented (short of reading the compiler's code)?

推荐答案

我用 i32 替换了 int,因为 int 现在已弃用.它已被替换为 isize,但这可能不是正确的类型.

I replaced int with i32, because int is now deprecated. It's been replaced with isize, but that's likely not the correct type.

编译器不会抱怨闭包的参数;它在抱怨关闭本身.您需要为闭包指定生命周期.

The compiler is not complaining about the closure's parameter; it's complaining about the closure itself. You need to specify a lifetime for the closure.

fn bar<'a>(x: i32) -> (|i32|:'a -> i32) {
    |n: i32| -> i32 {
        if n < x { return n }

        x
    }
}

但它不起作用:

<anon>:13:16: 13:17 error: captured variable `x` does not outlive the enclosing closure
<anon>:13         if n < x { return n }
                         ^
<anon>:11:41: 17:2 note: captured variable is valid for the block at 11:40
<anon>:11 fn bar<'a>(x: i32) -> (|i32|:'a -> i32) {
<anon>:12     |n: i32| -> i32 {
<anon>:13         if n < x { return n }
<anon>:14 
<anon>:15         x
<anon>:16     }
          ...
<anon>:11:41: 17:2 note: closure is valid for the lifetime 'a as defined on the block at 11:40
<anon>:11 fn bar<'a>(x: i32) -> (|i32|:'a -> i32) {
<anon>:12     |n: i32| -> i32 {
<anon>:13         if n < x { return n }
<anon>:14 
<anon>:15         x
<anon>:16     }
          ...

那是因为闭包试图通过引用捕获 x,但闭包比 x 的生命周期长,这是非法的(同样,返回引用也是非法的)到 x).

That's because the closure tries to capture x by reference, but the closure outlives x, which is illegal (in the same way it would be illegal to return a reference to x).

让我们尝试使用 proc.proc 通过移动捕获值.

Let's try using a proc. A proc captures values by move.

proc 已从语言中删除,因为此答案最初是编写的.

proc has been removed from the language since this answer was originally written.

fn main() {
    let foo = bar(8);

    println!("Trying `foo` with 4: {:d}", foo(4));
    println!("Trying `foo` with 8: {:d}", foo(8));
    println!("Trying `foo` with 13: {:d}", foo(13));
}

//

fn bar<'a>(x: i32) -> (proc(i32):'a -> i32) {
    proc(n: i32) -> i32 {
        if n < x { return n }

        x
    }
}

不幸的是,这也不起作用.

Unfortunately, that doesn't work either.

<anon>:5:43: 5:46 error: use of moved value: `foo`
<anon>:5     println!("Trying `foo` with 8: {:d}", foo(8));
                                                   ^~~
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
<anon>:5:5: 5:51 note: expansion site
<anon>:4:43: 4:46 note: `foo` moved here because it has type `proc(i32) -> i32`, which is non-copyable (perhaps you meant to use clone()?)
<anon>:4     println!("Trying `foo` with 4: {:d}", foo(4));
                                                   ^~~

您只能调用一次 proc.调用消耗了闭包.

You can only call a proc once. The call consumes the closure.

现在正确的解决方案是使用未装箱"的闭包:

The correct solution now is to use "unboxed" closures:

fn main() {
    let foo = bar(8);

    println!("Trying `foo` with 4: {}", foo(4));
    println!("Trying `foo` with 8: {}", foo(8));
    println!("Trying `foo` with 13: {}", foo(13));
}

//

fn bar(x: i32) -> Box<Fn(i32) -> i32 + 'static> {
    Box::new(move |&: n: i32| -> i32 {
        if n < x { return n }

        x
    })
}

输出:

Trying `foo` with 4: 4
Trying `foo` with 8: 8
Trying `foo` with 13: 8

这篇关于Rust 函数返回一个闭包:“需要明确的生命周期绑定";的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆