无法移出 `FnMut` 闭包中捕获的变量 [英] Cannot move out of captured variables in an `FnMut` closure

查看:59
本文介绍了无法移出 `FnMut` 闭包中捕获的变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

pub fn create_future(通知器:mpsc::Sender,代理:代理,) ->impl Future{proxy.something().and_then(移动|子| {sub.for_each(move |a| {//<---- 关闭 Aproxy.something_else(a).and_then(move |b| {//<---- 关闭 Bnotificator.send(b.len());//<---- 错误!好的(())}).or_else(|e| {恐慌!(哎呀{}",e);好的(())})})}).map_err(|e| {()})}

这不会编译,因为

.and_then(move |b| {^^^^^^^^^ 无法移出`FnMut` 闭包中捕获的外部变量

我对错误的理解是:

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

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

解决方案

嵌套闭包很棘手.

考虑一下:

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) {println!("字节数:{:?}", f());}fn 主(){use_a_fn_multiple_times(|a: 字符串| {use_fn_once(move || a.into_bytes());});}

游乐场

请注意,内部闭包通过移动捕获了 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) {println!("字节数:{:?}", f());}fn 主(){让outer_s = "见:".to_owned();use_a_fn_multiple_times(|a: 字符串| {use_fn_once(移动 || {让 mut v = outer_s.into_bytes();v.extend(a.into_bytes());v});});}

游乐场>

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

要将其映射回您的代码,说闭包 B 捕获 notificator"是错误的,因为闭包 B 不只一个.有必要的数量,但通常您嵌套的 and_thenfor_each 调用将在这段代码中结束.但只有一个人可以通过移动捕捉.

所以要解决这个问题,你要么需要确保只有一个闭包 B,要么确保你有足够的 mpsc::Sender 供所有人使用.

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

let closure_b = 移动 |b|{notificator.send(b.len());好的(())};proxy.something().and_then(移动|子| {sub.for_each(move |a| {//<---- 关闭 Aproxy.something_else(a).and_then(closure_b).or_else(|e| {恐慌!(哎呀{}",e);好的(())})})}).map_err(|e| {()})

除此之外不起作用,因为现在Closure A面临同样的问题,所以你必须多次这样做:

let closure_b = 移动 |b|{notificator.send(b.len());好的(())};让closure_a = 移动|a|{proxy.something_else(a).and_then(closure_b).or_else(|e| {恐慌!(哎呀{}",e);好的(())})};proxy.something().and_then(移动|子| {sub.for_each(closure_a)}).map_err(|e| {()})

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

当一切都完成后,您的代码仍然会失败,因为您在尝试使用 Proxy 的同时还尝试使用它.

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

This doesn't compile because

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

My understanding of the error is:

  1. Closure B is FnMut, and it captures notificator by taking its ownership
  2. In Closure B, send again needs to take the ownership
  3. Now both send and Closure B are modifying notificator thus the error.

Is my understanding right? How can I solve this problem?

解决方案

Nested closures are tricky.

Consider this:

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

Notice that the inner closure captures a by move. This is fine. The outer closure owns a and can do with it what it wants, including moving it into the inner closure (which, because it consumes its captured value, is a FnOnce).

The outer closure is called multiple times, each time with a new string, and every time a new inner closure capturing this string is created.

But what if what you want to capture comes from even further out?

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

Then you get the error you're seeing (except for Fn vs FnMut, which is immaterial to the problem). The inner closure is created anew on every call to the outer closure (it has to be, because it has to capture a every time), but it tries to capture outer_s by move every time. This can't work; after the first time, outer_s is moved from and thus invalid.

To map this back to your code, it's wrong to say "Closure B captures notificator", because there isn't just one Closure B. There's as many as necessary, however often your nested and_then and for_each calls will end up in that piece of code. But only one can ever capture by move.

So to solve this, you either need to make sure there's only one Closure B, or make sure you have enough mpsc::Senders for everyone.

The first way is done by pulling the closure out of the nested context.

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

except that won't work, since now Closure A faces the same issue, so you have to do it multiple times:

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

The second way involves a lot of clone() calls, and since I can't type-check your code, I won't attempt to write it.

When all is said and done, though, your code will still fail, because you're moving out of Proxy while also trying to use it.

这篇关于无法移出 `FnMut` 闭包中捕获的变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆