Functional Rust:为什么 `filter` 在闭包中借用?
Functional Rust: Why is `filter` borrowing in closure?
我正在尝试练习使用 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
二维矩阵。
我对引擎盖下发生的事情的心智模型似乎不充分。
I don't see why filter borrows the levels 2D matrix. My mental model of what is happening under the hood seems inadequate.
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 still 是借用的,答案是 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.f
和 Filter.f
同时存在并且需要相同的数据,如果两者都只需要从中读取就没问题,但显然 count_something
(因此 map
)需要一个可变(唯一)引用,这与只读(共享)引用不兼容。
我正在尝试练习使用 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
二维矩阵。
我对引擎盖下发生的事情的心智模型似乎不充分。
I don't see why filter borrows the levels 2D matrix. My mental model of what is happening under the hood seems inadequate.
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 still 是借用的,答案是 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.f
和 Filter.f
同时存在并且需要相同的数据,如果两者都只需要从中读取就没问题,但显然 count_something
(因此 map
)需要一个可变(唯一)引用,这与只读(共享)引用不兼容。