Generic higher-order function syntax

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?

Like 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.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.