将值传出迭代器而不停止迭代

问题描述 投票:0回答:1

我有一个解析输入字符串的函数:

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) 部分不起作用。这是我试图解决这个问题的尝试。但其他一切都有效。

dictionary rust iterator mapping
1个回答
0
投票

如果你稍微重构你的迭代器,你可以做你想做的事。


但是我们首先要解决问题是什么。

作为一个更简单的示例,让我们计算所有大写字母(以更愚蠢的方式):

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

当然可以有多种写法。

© www.soinside.com 2019 - 2024. All rights reserved.