The rust function returns a closure: "explicit binding to lifetime is required"

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 following error.

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

I pass the integer argument bar by value. Why does Rust care about the life of an integer passed by value? What is the correct way to write 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 captured environment.

Is there an additional specification of how the compiler indicates whether to write external variables by reference, copy or move them? What are the evaluation criteria and what is their application? Is this documented (not counting the compiler code)?

+4
source share
2 answers

EDIT: I replaced int with i32 because int now deprecated. It has been replaced by isize , but this is probably not the right type.

The compiler does not complain about the close parameter; he complains about the closure itself. You need to specify the service life for closing.

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

But this does not 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 } ... 

This is because a closure tries to capture x by reference, but a closure survives x , which is illegal (it would also be illegal to return a reference to x ).

Try using proc . A proc captures values ​​by moving.

EDIT: proc been removed from the language since this answer was originally recorded.

 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, this does not 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 call proc once. The call requires closure.

The correct solution now is to use the “unpacked” 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 
+10
source

Is there an additional specification of how the compiler indicates whether to write external variables by reference, copy or move them? What are the evaluation criteria and what is their application? Is this documented (not counting the compiler code)?

Let me complement Francis answer:

Closures such as |x| a*x+b |x| a*x+b , always capture their environment (for example, a and b here) by reference. In your case, these are functional-local variables, and Rust does not allow you to return such a closure, because these local-local variables will no longer exist. Let me thank the borrower for catching this mistake. The use cases for these closures usually pass them as parameters to other functions, rather than return them. However, if you do not get access to any other variables, such a closure allows you to survive the scope of the function in which it was created: ||:'static -> SomeType . The representation of these closures is just a couple of pointers. One that points to a function and one that points to the frame of the function stack (if something is captured by the link).

Closures that you write with proc always capture their environment, “acquiring” them (they move to the state of the closure object). Another property of these closures is that you can only call them once, because the associated function actually consumes the closure state. This is useful for running concurrent tasks. proc closure provides a heap allocation price because they save their state indirectly (just like Box does). The advantage of this is that the representation of proc closures (ignoring the boxed state) is just a couple of pointers. One pointer to a function and one pointer to box variables.

Then there are so-called unpacked locks. As far as I know, unpacked shutters are still considered experimental. But they allow you to do exactly what you want - not directly, but in a box. Unlocked shutters also capture their surroundings by value. But unlike proc closures, heap allocation is not involved. They store their variables directly. You can think of them as a structure with a unique non-covered type that implements one of the following characteristics: Fn , FnMut or FnOnce one method that takes its own parameter as &self , &mut self or self . Unlocked closures are good because they lack the level of indirection for the function and variables, which makes it possible to better embed and, therefore, increase productivity. But at the moment it is not possible to write a function that directly returns such an unpacked closure. One solution is to insert an unpacked closure, as shown in the last section of this Francis answer.

+4
source

Source: https://habr.com/ru/post/977086/


All Articles