我有一个解析输入字符串的函数:
fn parse_input(s: &str) -> ((usize, usize), BTreeMap<(usize, usize), Tile>){
let mut start: (usize, usize) = (0,0);
let grid = s.split("\n").enumerate().flat_map(|(r,l)| {
l.chars().enumerate().map(move |(col, c)| {
let t = classify_tile(c);
match t {
Tile::Start => {
*start = (r, col);
((r,col), t)
},
_ => ((r,col), t)
}
})
}).collect::<BTreeMap<(usize, usize), Tile>>();
(start, grid)
}
我基本上想捕获起始图块的 r 和 col 值(这是唯一的,仅出现一次)。但目前,如果我尝试修改迭代器内部的元组,我假设由于借用和范围原因,该值不会在迭代器外部修改。 不过,迭代器完成很重要。
另一种解决方案是随后在 btreemap 中搜索起始图块,但我希望有一个更有效的解决方案。
我应该将其作为嵌套 for 循环吗?这里的迭代实际上是否更有效?
编辑:classify_tile 函数返回枚举类型。开始,土壤或管道。 *start = (r,col) 部分不起作用。这是我试图解决这个问题的尝试。但其他一切都有效。
如果你稍微重构你的迭代器,你可以做你想做的事。
但是我们首先要解决问题是什么。
作为一个更简单的示例,让我们计算所有大写字母(以更愚蠢的方式):
let text = "Hello World";
let mut count = 0;
text.split_whitespace()
.flat_map(|s| {
s.chars().map(|c| {
if c.is_uppercase() {
count += 1;
}
})
})
.for_each(|_| {});
assert_eq!(count, 2);
如果我们尝试编译它,我们会遇到与您相同的问题:
error: captured variable cannot escape `FnMut` closure body
--> src\main.rs:85:13
|
80 | let mut count = 0;
| --------- variable defined here
...
84 | .flat_map(|s| {
| - inferred to be a `FnMut` closure
85 | / s.chars().map(|c| {
86 | | if c.is_uppercase() {
87 | | count += 1;
| | ----- variable captured here
88 | | }
89 | | })
| |______________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
这是为什么?
给
map()
的闭包需要可变借用count
,这本身就很好。
当我们将
flat_map()
加入其中时,问题就出现了。因为 flat_map()
可能会产生多个迭代器。每个生成的迭代器都需要可变借用 count
。这当然是不允许的,因为我们不能多次借用任何东西。
总的来说,这个问题很容易解决,你只需重构迭代器即可:
text.split_whitespace()
.flat_map(|s| s.chars())
.map(|c| {
if c.is_uppercase() {
count += 1;
}
})
.for_each(|_| {});
// or
text.split_whitespace()
.map(|s| s.chars())
.flatten()
.map(|c| {
if c.is_uppercase() {
count += 1;
}
})
.for_each(|_| {});
为你的迭代器解决这个问题,可能看起来像这样:
let grid = s
.split("\n")
.enumerate()
.flat_map(|(r, l)| {
l.chars().enumerate().map(move |(col, c)| {
let t = classify_tile(c);
match t {
Tile::Start => ((r, col), t),
_ => ((r, col), t),
}
})
})
.inspect(|((r, col), t)| match t {
Tile::Start => {
start = (*r, *col);
}
_ => {}
})
.collect::<BTreeMap<(usize, usize), Tile>>();
当然可以有多种写法。