Function pointer to generic function


#1

I have a function like

fn my_sum<'a, I>(vals: I) -> f64
where
    I: Iterator<Item = &'a f64>,
{
    vals.sum()
}

I have another function that should return a pointer to this function

fn get_aggreation_fn() ->  ...   {
        return my_sum;
}

What would be the return signature of this function be ?

Thanks


#2

Is this supposed to be a Rust fn pointer? If so, you can do something like:

fn get_aggregation_fn<'a, I: Iterator<Item=&'a f64>>() -> fn(I) -> f64 {
        my_sum::<I>
}

But why do you need a fn pointer?

Edit: body can be just my_sum - inference will handle the type deduction.


#3

Thanks.

I wanted to a have the following functions that operate on f64 iterators
sum, avg, min, max

I am using rust-postgres to get a large number of rows out of the db and then using itertools::group_by
to give me a group of f64 series to perform those functions on. I want the particular function to be chosen before hand.

so ideally (The following is not tested) I want something like

let my_aggregation = get_aggregation_fn(Aggregate::SUM);

let data = rows.into_iter().map( |row| (row.get(0), row.get(1), row.get(2)))
.group_by(|t| (t.1, BmosConnection::floor_by_granularity(t.0, time_granularity)))
.map(|key, values| my_aggregation(values))

I do this instead of in Postgres because for large amounts of data it is much faster.
I also have a diff aggregation that requires getting all the data from postgres first so have to use this method.


#4

I think you can work with closures. Either specify them inline/around the place you’re going to invoke them, or you can return one (instead of a fn pointer). Given impl Trait is about to land, you’ll be able to write the following (as an example) in stable:

fn get_sum_agg<'a, I: Iterator<Item = &'a f64>>() -> impl FnMut(I) -> f64 {
    |vals| vals.sum()
}

#5

In this case how would I assign to a variable.

Ie

let aggregate_fn = get_aggregation_fn(aggregate);

produces

let aggregate_fn = get_aggregation_fn(aggregate);
     |                    ------------   ^^^^^^^^^^^^^^^^^^ cannot infer type for `I`
     |                    |
     |                    consider giving `aggregate_fn` a type

#6

You’d let type inference figure it out if you can. For example: play.

If type inference can’t deduce it, you’ll have to specify a Iterator type using turbofish.


#7

Thank you very much. I can honestly say I don’t think I would have worked that out even with many days trying.


#8

Why is it that when you move

let mut agg = get_agg(Agg::Sum);

into another method you get the error

error[E0283]: type annotations required: cannot resolve _: std::clone::Clone

why does rust care ?

As shown below

https://play.rust-lang.org/?gist=3749057ebfc28dc2c951bb9d20ac8cde&version=nightly


#9

Because it cannot possibly know what I is. You don’t tell it, and you never use agg. The error message is a symptom of it not knowing what I is.

The compiler isn’t psychic: in order to infer types, it has to have something to work from.


#10

Ah because I was not using it. Thanks
It surprises me that simply printing with

println!("{}", agg(v.iter()));

allows the compiler to infer


#11

For reference. I finally got things going below. Thanks for all the help.

let data : Vec<(_,_)> = rows.into_iter()
                              .map( |row| (row.get::<_, DateTime<Utc>>(0), row.get::<_, i32>(1), row.get::<_, f64>(2)))
                              .group_by(|t| (t.1, BmosConnection::floor_by_granularity(t.0, time_granularity)))
                              .map(|group| (group.0, group.1 )).collect();

for (key, group) in data {

	let values : Vec<f64> = group.iter().map(|v| v.2).collect();

	let mut agg = get_agg(Aggregate::SUM);
	let result : f64 = agg(values.iter());

	println!("key {:?} ---  {:?} ---- {} ", key, values, result);
}

Interestingly I hit a weird issue ?
If vec here is defined after agg the compile fails. Ie

        let v = vec![2.0, 4.0];
        let mut agg = get_agg(Aggregate::SUM);
        //  let v = vec![2.0, 4.0];  Fails in this position
        let r : f64 = agg(v.iter());

Issue is https://github.com/rust-lang/rust/issues/38915 I believe.
I don’t seem to be very lucky with this piece of code but have learnt a few things :):grinning:


#12

Gut feeling is that this is because of the lifetime used for get_agg. The compiler can’t use a lifetime that begins after get_agg gets run, and the lifetime has to support v.iter(), so v must exist before get_agg is called so that the borrow can retroactively extend backward to before get_agg is called.


#13

As @DanielKeep said, the created closure creates a scope, 'a, for which the eventual borrow (when the closure is called) must be valid. This requires the f64s to be created prior to where the closure is created.

There’s no real need to have the aggregation functions working on f64 references - they should work on iterators that return f64 values, which removes the scope issue. If you have iterator iter that returns &f64, then iter.cloned() gives you an iterator that returns f64 values and this is of course a cheap transformation.


#14

I see closure pointer in https://tomassedovic.github.io/roguelike-tutorial/part-6-going-berserk.html, not sure if that’s similar to function pointer. Hope that helps.


#15

That looks like an ordinary fn pointer just dressed up a bit :slight_smile: