简体   繁体   中英

function returns a closure which return a closure that use the environment variable

functionA returns closureA , and that closureA returns a closureB , closureB which use variable from functionA 's surrounding environment.

fn main () {
   type Closure1 = Box<Fn() -> ()>;
   type Closure2 = Box<Fn() -> Closure1>;

   fn closure_container() -> Closure2 {
       let mut a: Vec<usize> = Vec::new();
       let closure2: Closure2 = Box::new(move || {
           let closure1 = || {
               println!("{}", a)
           };
           Box::new(closure1)
       });

       closure2
   }
}
error[E0507]: cannot move out of captured outer variable in an `Fn` closure
 --> src/main.rs:9:27
  |
6 |        let mut a: Vec<usize> = Vec::new();
  |            ----- captured outer variable
...
9 |            let closure1 = move || {
  |                           ^^^^^^^ cannot move out of captured outer variable in an `Fn` closure

It compiles let mut a = 100; , But let mut a: Vec<usize> = Vec::new(); will report a error! I don't know how to fix it.

You have (rightly) used move for the first closure (line 7), but you need to also add it for the second closure (line 8):

let closure2: Closure2 = Box::new(move || {
    let closure1 = move || { // <-- Add "move" on this line
        println!("{}", a)
    };
    Box::new(closure1)
});

playground

This makes it work if a has a Copy type, but it causes the error cannot move out of captured outer variable in an 'Fn' closure when a is not Copy (eg if a is a Vec ). The problem is due to the fact that you are defining closure2 as Fn , which means that you are telling the compiler that you might want to call it more than once. However the first time you call closure2 will move a into the returned closure1 , so a won't be available for a possible next call to closure2 .

Long story short: you need to define closure2 as FnOnce to tell the compiler that you can't call it more than once, or you need to move a clone of a into closure1 so that closure2 will keep its copy.

Solution 1: FnOnce

type Closure1 = Box<Fn() -> ()>;
type Closure2 = Box<FnOnce() -> Closure1>;

fn closure_container() -> Closure2 {
    let a: Vec<usize> = Vec::new();
    let closure2: Closure2 = Box::new(move || {
        let closure1 = move || {
            println!("{:?}", a)
        };
        Box::new(closure1)
    });

    closure2
}

playground

Note however that although you can create a Closure2 this way, it is impossible to call it in current stable Rust. If you are willing to use nightly, it should work if you replace FnOnce with FnBox , but I'm hitting another error with this ( playground ). For the time being, you will need to use solution 2 and clone a . If you want to avoid the cost of cloning the entire vector, you can wrap it in an Rc and clone that ( playground ).

Solution 2: Clone

type Closure1 = Box<Fn() -> ()>;
type Closure2 = Box<Fn() -> Closure1>;

fn closure_container() -> Closure2 {
    let a: Vec<usize> = Vec::new();
    let closure2: Closure2 = Box::new(move || {
        let b = a.clone();
        let closure1 = move || {
            println!("{:?}", b)
        };
        Box::new(closure1)
    });

    closure2
}

playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM