I've a books: &mut Vec<u32>
. I want to pass to a function, such that, given a closure, the function either returns all the indices that have non-zero values, or the non-zero values.
For example:
books: [0, 1, 1]
will return [1, 2]
for indices, and [1, 1]
for values.
My attempt, that doesn't compile:
fn find_non_zero<T>(books: &[u32], f: fn(usize, &u32) -> T) -> T {
books
.iter()
.enumerate()
.filter(|(_, &count)| count > 0)
.map(f)
.collect()
}
Call it for values: find_non_zero(books, |(_, &count)| count)
Call it for indices: find_non_zero(books, |(b, _)| b)
What is the correct syntax for this?
Why fn(usize, &u32) -> T
doesn't work, but FnMut((usize, &u32)) -> T
does?
I realize have a typo there, the fn signature I'd shown doesn't accept a tuple, but in my code it does. I missed a ()
.
This compiles and does what I think you were asking for:
fn find_non_zero<'a, T: 'a>(
books: &'a [u32],
f: fn((usize, &u32)) -> T,
) -> impl Iterator<Item = T> + 'a {
books
.iter()
.enumerate()
.filter(|&(_, count)| *count > 0)
.map(f)
}
Most of the adjustments I had to make were to get the reference layers to work out, and the fact that map
is expecting a function that accepts a single parameter of (usize, &u32)
rather than two parameters.
You didn't specify what the output type should be, so I removed the collect()
and returned an Iterator of whatever the provided function outputs. Because the iterator has to capture a reference to the original slice, this requires all the lifetime bounds. If you returned a Vec
like in the other answer, it would not be necessary because the iterator wouldn't live longer than the call
2 Likes
Incidentally you can check the docs to see that map
expects a FnMut(Self::Item) -> B
(which only takes one argument).
If you want to allow multiple arguments (like your original bounds, but not like your original closures), you would need to do something like
// bind f as mutable and then
.map(|(b, c)| f(b, c)) // go from one argument (a 2-tuple) to two arguments
(There is no auto-splatting of tuples.)
From the previous replies, I recommend returning the iterator unless you have some reason not to, but using the FnMut
bound (which will accept closures the fn
function pointer version cannot, and avoid some indirection).
1 Like
Understood, and thanks, as always.