无法移出 `FnMut` 闭包中捕获的变量

qwe*_*iop 4 rust

pub fn create_future(
    notificator: mpsc::Sender<usize>,
    proxy: Proxy,
) -> impl Future<Item = (), Error = ()> {
    proxy.something()
        .and_then(move |sub| {
            sub.for_each(move |a| { // <---- Closure A
                proxy.something_else(a)
                    .and_then(move |b| { // <---- Closure B
                        notificator.send(b.len());  // <---- Error!
                        Ok(())
                    })
                    .or_else(|e| {
                        panic!("oops {}", e);
                        Ok(())
                    })
            })
        })
        .map_err(|e| {
            ()
        })
}
Run Code Online (Sandbox Code Playgroud)

这不会编译,因为

pub fn create_future(
    notificator: mpsc::Sender<usize>,
    proxy: Proxy,
) -> impl Future<Item = (), Error = ()> {
    proxy.something()
        .and_then(move |sub| {
            sub.for_each(move |a| { // <---- Closure A
                proxy.something_else(a)
                    .and_then(move |b| { // <---- Closure B
                        notificator.send(b.len());  // <---- Error!
                        Ok(())
                    })
                    .or_else(|e| {
                        panic!("oops {}", e);
                        Ok(())
                    })
            })
        })
        .map_err(|e| {
            ()
        })
}
Run Code Online (Sandbox Code Playgroud)

我对错误的理解是:

  1. 闭包 B 是FnMut,它notificator通过获取其所有权来捕获
  2. 在 Closure B 中,send再次需要取得所有权
  3. 现在send和 Closure B 都在修改notificator错误。

我的理解对吗?我怎么解决这个问题?

Seb*_*edl 9

嵌套闭包很棘手。

考虑一下:

fn use_a_fn_multiple_times(f: impl Fn(String)) {
    f("foo".to_owned());
    f("bar".to_owned());
}

fn use_fn_once(f: impl FnOnce() -> Vec<u8>) {
    println!("Bytes: {:?}", f());
}

fn main() {
  use_a_fn_multiple_times(|a: String| {
    use_fn_once(move || a.into_bytes());
  });
}
Run Code Online (Sandbox Code Playgroud)

操场

请注意,内部闭包a通过移动捕获。这可以。外部闭包拥有a并可以用它做它想做的事情,包括将它移动到内部闭包中(因为它消耗了它捕获的值,所以是 a FnOnce)。

外部闭包被多次调用,每次使用一个新字符串,并且每次创建一个新的内部闭包捕获该字符串。

但是如果你想捕捉的东西来自更远的地方呢?

fn use_a_fn_multiple_times(f: impl Fn(String)) {
    f("foo".to_owned());
    f("bar".to_owned());
}

fn use_fn_once(f: impl FnOnce() -> Vec<u8>) {
    println!("Bytes: {:?}", f());
}

fn main() {
  let outer_s = "see:".to_owned();

  use_a_fn_multiple_times(|a: String| {
    use_fn_once(move || {
        let mut v = outer_s.into_bytes();
        v.extend(a.into_bytes());
        v
    });
  });
}
Run Code Online (Sandbox Code Playgroud)

操场

然后你会看到你看到的错误(除了Fnvs FnMut,这对问题无关紧要)。每次调用外部闭包时都会重新创建内部闭包(它必须如此,因为它a每次都必须捕获),但它outer_s每次都尝试通过移动来捕获。这是行不通的;第一次后,outer_s被移出并因此无效。

要将其映射回您的代码,说“闭包 B 捕获notificator”是错误的,因为不只有一个闭包 B。有多少是必要的,但通常您的嵌套and_thenfor_each调用最终会出现在那段代码中。但只有一个人可以通过移动来捕捉。

所以要解决这个问题,你要么需要确保只有一个闭包 B,要么确保mpsc::Sender每个人都有足够的s。

第一种方法是将闭包从嵌套上下文中拉出来。

let closure_b = move |b| {
    notificator.send(b.len());
    Ok(())
};
proxy.something()
    .and_then(move |sub| {
        sub.for_each(move |a| { // <---- Closure A
            proxy.something_else(a)
                .and_then(closure_b)
                .or_else(|e| {
                    panic!("oops {}", e);
                    Ok(())
                })
        })
    })
    .map_err(|e| {
        ()
    })
Run Code Online (Sandbox Code Playgroud)

除了这行不通,因为现在 Closure A 面临同样的问题,所以你必须多次这样做:

let closure_b = move |b| {
    notificator.send(b.len());
    Ok(())
};
let closure_a = move |a| {
    proxy.something_else(a)
        .and_then(closure_b)
        .or_else(|e| {
            panic!("oops {}", e);
            Ok(())
        })
};
proxy.something()
    .and_then(move |sub| {
        sub.for_each(closure_a)
    })
    .map_err(|e| {
        ()
    })
Run Code Online (Sandbox Code Playgroud)

第二种方式涉及大量clone()调用,由于我无法对您的代码进行类型检查,因此我不会尝试编写它。

然而,当一切都说完了,你的代码仍然会失败,因为你在Proxy尝试使用它的同时也会离开。