Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rust function returning a closure: ``explicit lifetime bound required"

Tags:

rust

The following code does not compile.

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
    }
}

The error is as following.

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

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.

EDIT

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)?

like image 724
JONNALAGADDA Srinivas Avatar asked Dec 07 '22 00:12

JONNALAGADDA Srinivas


1 Answers

EDIT: 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
    }
}

But it doesn't work:

<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     }
          ...

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).

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

EDIT: 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));
                                                   ^~~

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
    })
}

Output:

Trying `foo` with 4: 4
Trying `foo` with 8: 8
Trying `foo` with 13: 8
like image 195
Francis Gagné Avatar answered May 25 '23 21:05

Francis Gagné