功能性Rust:为什么`filter`在闭包中要借用?

3

我想练习使用Rust的函数式能力。

例如,我想将这个循环转换为函数式:

levels: Vec<Vec<u8>> = //< Built by something

let mut total = 0;
for (x, y) in iproduct!(0..10, 0..10) {
    if levels[x][y] > 9 {
        total += count_something(levels, x, y);
    }
}

// Edit: here's the `count_something` function signature
fn count_something (levels: &mut Vec<Vec<u8>>, x: usize, y: usize) -> usize {
    // Count
}

这是我的功能重构的结果:
iproduct!(0..10, 0..10)
    .filter(|(x, y)| levels[*x][*y] > 9)
    .map(|(x, y)| count_something(levels, x, y))
    .sum()

问题是:这段代码无法编译。
错误信息:error[E0500]: closure requires unique access to *levels but it is already borrowed
我不理解为什么filter会借用levels二维矩阵。 我的内部模型似乎无法解释这个问题。

你能否写一个 [MRE] ?没有更多信息很难帮助你,例如 count_something 如何声明(如果它需要可变引用,那么你就有了答案)。 - Denys Séguret
嗨@DenysSéguret。我同意你的观点,提出好问题是我能做的最少的事情。话虽如此,我没有包括count_something,因为我认为这是不必要的细节。我错了吗? - gberth
我在问题中添加了函数签名。 - gberth
请注意,函数式编程与可变数据的兼容性较差(这不仅适用于Rust,而是任何语言都是如此)。在纯函数式风格中,“count_something”将使用对“levels”的不可变引用,并返回修改后的副本。 - Jmb
@Jmb 有趣的观点。我没有考虑到不可变性。 - gberth
1个回答

7
我不明白为什么`filter`要借用二维矩阵的级别。我的理解是在底层发生了什么似乎还不够充分。

`filter`的回调函数需要访问矩阵,所以这里进行了借用(另一个选择是移动):在Rust中,闭包是语法糖,对应一个结构体和一个可调用对象,任何自由变量都会自动转换为匿名结构体的成员:

.filter(|(x, y)| levels[*x][*y] > 9)

成为(更多或更少,有很多被跳过的内容)。
struct $SECRET1$ {
    levels: &Vec<Vec<u8>>
}
impl $SECRET1$ {
    fn call(&self, x: &usize, y: &usize) -> bool {
        self.levels[*x][*y] > 9
    }
}
[...]
.filter($SECRET1$ { levels: &levels })

这就是为什么 levels 被借用的原因。

问题的另一部分是,当地图运行时,为什么 levels 仍然被借用,答案是 Rust 的迭代器是惰性的,所以它们并发地(交错地)运行操作,而不是顺序地运行。

因此,当你编写以下代码时:

iproduct!(0..10, 0..10)
    .filter(|(x, y)| levels[*x][*y] > 9)
    .map(|(x, y)| count_something(levels, x, y))
    .sum()

您正在编写类似以下内容的内容:

let p = iproduct!(0..10, 0..10);
let f = Filter {
    f: |(x, y)| levels[*x][*y] > 9,
    iter: p
};
let m = Map {
    f: |(x, y)| count_something(levels, x, y),
    iter: f
};
Iterator::sum(m)

因此,正如您所看到的,Map.fFilter.f同时存在并需要相同的数据。如果两者都只需要从中读取数据,那就没问题了,但显然count_something(因此map)需要一个可变(唯一)引用,这与只读(共享)引用不兼容。


非常有帮助。谢谢。 - gberth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接