【发布时间】:2021-09-01 13:42:45
【问题描述】:
这是我遇到的问题的 MWE,它无法编译:
use std::collections::HashSet;
use nom::{
IResult,
error::VerboseError,
bytes::complete::is_not,
character::complete::space1,
combinator::map,
multi::separated_list1,
};
type Set = HashSet<char>;
fn _make_parser(reducer: impl Fn(Set, Set) -> Set) -> impl Fn(&str) -> IResult<&str, Set, VerboseError<&str>> {
move |input| {
map(
separated_list1(
space1,
is_not(" \t"),
),
|vec: Vec<&str>| {
vec.iter().map(|s| s.chars().collect::<Set>())
.reduce(reducer).unwrap()
}
)(input)
}
}
fn main() {
println!("Do nothing");
}
尽管不重要,_make_parser 旨在创建并返回一个 nom 解析器函数,该函数将采用空格分隔的字符串集,如下所示:
abc acdsodin ac azcefgd
然后解析器应将每个字符串转换为char 的集合(即HashSet),并在集合中应用一些操作,作为reducer 闭包传递给_make_parser。此闭包旨在调用类似HashSet::union 或HashSet::intersection。
代码编译失败,出现以下错误:
error[E0507]: cannot move out of `reducer`, a captured variable in an `FnMut` closure
--> src/main.rs:21:33
|
12 | fn _make_parser(reducer: impl Fn(Set, Set) -> Set) -> impl Fn(&str) -> IResult<&str, Set, VerboseError<&str>> {
| ------- captured outer variable
...
21 | .reduce(reducer).unwrap()
| ^^^^^^^ move occurs because `reducer` has type `impl Fn(Set, Set) -> Set`, which does not implement the `Copy` trait
error[E0507]: cannot move out of `reducer`, a captured variable in an `Fn` closure
--> src/main.rs:19:17
|
12 | fn _make_parser(reducer: impl Fn(Set, Set) -> Set) -> impl Fn(&str) -> IResult<&str, Set, VerboseError<&str>> {
| ------- captured outer variable
...
19 | |vec: Vec<&str>| {
| ^^^^^^^^^^^^^^^^ move out of `reducer` occurs here
20 | vec.iter().map(|s| s.chars().collect::<Set>())
21 | .reduce(reducer).unwrap()
| -------
| |
| move occurs because `reducer` has type `impl Fn(Set, Set) -> Set`, which does not implement the `Copy` trait
| move occurs due to use in closure
很明显,所有操作都在_make_parser 函数中,并且我们有很多闭包正在进行。我们将_make_parser返回的闭包称为parser闭包,将传递给nommap组合器的闭包称为map闭包。
我认为正在发生的事情是,reducer 闭包(或更准确地说是与闭包关联的隐式环境结构)被移动到更靠近解析器的位置,因为我们使用了 move 关键字.这就是我们想要的,因为我们需要 reducer 闭包来延长 _make_parser 函数的寿命。然后似乎reducer 也被移动到 map 闭包中,这也很好,因为除了 map 闭包之外,我们在解析器闭包中不需要它。似乎reduce 方法也试图获得reducer 的所有权(我试图从地图关闭中借用它,但它不会接受)。这似乎不应该是一个问题,因为 map 闭包实际上只被调用一次。然而,map 组合器采用的闭包类型是 FnMut,因为在某些情况下,它可能会被多次调用(例如,如果 map 组合器嵌套在重复组合器中)。我怀疑这是有问题的,因为 reducer 被移动到 reduce 方法中,这排除了多次调用 map 闭包。
我对错误消息的解释是否正确,还是我对情况的理解有误?如果是这样,如何解决?似乎可以使用引用计数来支持多个所有者来解决它,但是由于 reduce 需要一个闭包而不是 Rc<closure> 我无法让它工作。请注意,我只是在学习 Rust,还没有真正使用过引用计数器,尽管我认为我基本上了解它们是如何工作的。
最后,请注意,如果我将_make_parser 中的reducer 类型更改为普通的fn 而不是闭包Fn,我认为这是有道理的,因为在这种情况下没有隐式环境结构体担心移动。这甚至可以很好地用于我的实际目的,因为我实际上不需要在我的reducer 中捕获任何环境。但是,我读到接受闭包而不是普通函数更好(即更通用),而且我也很好奇如何使它与_make_parser 接受闭包一起工作,或者对于某些人来说这是不可能的原因。
【问题讨论】:
标签: rust