gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-29 08:12:08 24 4
gpt4 key购买 nike

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. 在Closure 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<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

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

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

所以要解决这个问题,您要么需要确保只有一个 Closure B,要么确保您有足够的 mpsc::Sender 给每个人。

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

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

除了那行不通,因为现在闭包 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 的同时尝试使用它。

关于rust - 无法移出 `FnMut` 闭包中捕获的变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53038935/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com