I'm trying to implement a function which will construct an iterator over the whole range of possible indices for several related 2D arrays (which always have the same size). However, the compiler is giving me an error, saying that the closure I'm passing to map
does not implement Fn
, only FnMut
. This is my original code:
pub fn get_index_iterator(&self) -> Map<Range<usize>, impl Fn(usize) -> (usize, usize)> {
(0usize..(self.width * self.height))
.map(|idx| -> (usize, usize) {
(idx % self.width, idx / self.width)
}
)
}
In an attempt to figure out what was going on, I eventually tried replacing that with this code:
pub fn get_index_iterator(&self) -> Map<Range<usize>, impl Fn(usize) -> (usize, usize)> {
(0usize..(self.width * self.height))
.map(|idx| -> (usize, usize) {
(0usize, 0usize)
}
)
}
Just to see if the error would change to something more helpful, but it did not, the error remains
expected a `std::ops::Fn<(usize,)>` closure, found `[closure@src\lib.rs:33:18: 33:41]`
the trait `std::ops::Fn<(usize,)>` is not implemented for closure `[closure@src\lib.rs:33:18: 33:41]`
`[closure@src\lib.rs:33:18: 33:41]` implements `FnMut`, but it must implement `Fn`, which is more general
for both versions. I cannot see how a closure returning a constant and doing nothing else could possibly mutate the state of it's environment. What am I missing here?
I don't know why this happens, but if you make the closure its own variable, it works in both cases.
pub fn get_index_iterator(&self) -> Map<Range<usize>, impl Fn(usize) -> (usize, usize) + '_> {
let f = |idx| -> (usize, usize) { (idx % self.width, idx / self.width) };
(0usize..(self.width * self.height)).map(f)
}
You need + '_
since it captures self
, but it's still a Fn
as it should be. So you're correct that this closure should work as Fn
. It might be that map
is downgrading the closure somehow, but then I don't know how passing it as a variable works.
Besides this, you could write this as impl Iterator
to avoid this entirely. This is preferred since if you want to optimize this later, you can change the iterator type without breaking your API. It is also shorter. You need mut
to call next
on Map
anyway, so making the closure Fn
instead of FnMut
doesn't provide any benefit.
pub fn get_index_iterator(&self) -> impl Iterator<Item = (usize, usize)> + '_
Here's a shorter version that shows the same behavior.
fn map<F: FnMut()>(f: F) -> F {
f
}
pub fn returns_map() -> impl Fn() {
// let c = || {};
// map(c)
map(|| {})
}