首页 > 解决方案 > 函数返回一个闭包,该闭包返回一个使用环境变量的闭包

问题描述

functionA返回closureA,并且closureA返回 a closureB,它使用来自周围环境的closureB变量。functionA

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

可以编译let mut a = 100;,但是let mut a: Vec<usize> = Vec::new();会报错!我不知道如何解决它。

标签: rust

解决方案


您(正确地)用于move第一个闭包(第 7 行),但您还需要为第二个闭包(第 8 行)添加它:

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

操场

a如果有类型,这使得它可以工作,但是当没有Copy类型时它会导致错误(例如,如果是 a )。问题是由于您将其定义为,这意味着您告诉编译器您可能要多次调用它。但是,您第一次调用将移至返回的,因此将无法用于可能的下一次调用.cannot move out of captured outer variable in an 'Fn' closureaCopyaVecclosure2Fnclosure2aclosure1aclosure2

长话短说:您需要定义closure2asFnOnce告诉编译器您不能多次调用它,或者您需要将克隆移至 ina以便closure1保留closure2其副本。

解决方案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
}

操场

但是请注意,尽管您可以通过这种方式创建一个,但在当前稳定的 Rust 中Closure2是不可能调用它的。如果您愿意每晚使用,如果您替换为 ,它应该可以工作FnOnceFnBox但我遇到了另一个错误(操场)。目前,您将需要使用解决方案 2 和 clone a。如果你想避免克隆整个向量的成本,你可以将它包装在一个Rc并克隆它操场)。

解决方案 2:克隆

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
}

操场


推荐阅读