【问题标题】:Cannot move out of captured variables in an `FnMut` closure无法在“FnMut”闭包中移出捕获的变量
【发布时间】:2019-04-01 23:56:16
【问题描述】:
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| {
            ()
        })
}

这不会编译,因为

.and_then(move |b| {
          ^^^^^^^^^ cannot move out of captured outer variable in an `FnMut` closure

我对错误的理解是:

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

我的理解对吗?我该如何解决这个问题?

【问题讨论】:

  • 什么是Proxy?请问可以提供MCVE吗?
  • “在闭包 B 中,再次发送需要取得所有权” - 这听起来不对。 mpsc::Sender::send 接受&amp;self。
  • 我相信How can I move a captured variable into a closure within a closure?的答案已经回答了您的问题。如果您不同意,请edit您的问题解释差异。否则,我们可以将此问题标记为已回答。

标签: rust


【解决方案1】:

嵌套闭包很棘手。

考虑一下:

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

Playground

请注意,内部闭包通过移动捕获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
    });
  });
}

Playground

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

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

所以要解决这个问题,您要么需要确保只有一个 Closure B,要么确保您有足够的 mpsc::Senders 供所有人使用。

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

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

除了那行不通,因为现在 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| {
        ()
    })

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

尽管如此,您的代码仍然会失败,因为您要离开Proxy,同时还要尝试使用它。

【讨论】:

  • 谢谢。非常有帮助。我最终通过添加clone()s 来修复它。
  • 或者在我的情况下,当我想使用需要在嵌套FnMut 中使用顶级参数调用的struct 时,我首先使用#[derive(Clone)] 注释该结构以便能够使用clone()。
猜你喜欢
  • 1970-01-01
  • 2020-01-16
  • 2016-02-13
  • 2020-10-14
  • 1970-01-01
  • 1970-01-01
  • 2022-11-19
  • 2015-04-15
相关资源
最近更新 更多