4

I'm trying to use a closure to call a function on a bunch of feature vectors. The most important code lines here are the first line, where we say what type query must be and the last line, where we try to give a closure.

fn stat_query(dataset: &Vec<Vec<i32>>, query: Fn(&Vec<i32>) -> f64) -> f64 {
    // takes in a dataset of feature vectors and a query,
    // returns the average of the query on the dataset
    if dataset.len() == 0 {
        0.
    } else {
        dataset.iter().map(|ref fv| query(&fv)).sum() / (dataset.len() as f64)
    }
}

fn main() {
    // build dataset
    let fv1: Vec<i32> = vec![1, 1, 1, 1, 1];
    let fv2: Vec<i32> = vec![1, 0, 1, 0, 1];
    let my_dataset = vec![fv1, fv2];

    // query checks whether sum of features is greater than a threshold
    fn my_query(ref fv: &Vec<i32>, threshold: i32) -> f64 {
        if fv.iter().sum() > threshold {
            1.
        } else {
            0.
        }
    }

    // run query on dataset with threshold 3
    println!("{}", stat_query(&my_dataset, |ref fv| my_query(fv, 3)));
}

When I run this, I get an error:

error[E0277]: the trait bound `for<'r> std::ops::Fn(&'r std::vec::Vec<i32>) -> f64 + 'static: std::marker::Sized` is not satisfied
 --> src/main.rs:1:40
  |
1 | fn stat_query(dataset: &Vec<Vec<i32>>, query: Fn(&Vec<i32>) -> f64) -> f64 {
  |                                        ^^^^^ `for<'r> std::ops::Fn(&'r std::vec::Vec<i32>) -> f64 + 'static` does not have a constant size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `for<'r> std::ops::Fn(&'r std::vec::Vec<i32>) -> f64 + 'static`
  = note: all local variables must have a statically known size

error[E0308]: mismatched types
  --> src/main.rs:27:44
   |
27 |     println!("{}", stat_query(&my_dataset, |ref fv| my_query(fv, 3)));
   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^ expected trait std::ops::Fn, found closure
   |
   = note: expected type `for<'r> std::ops::Fn(&'r std::vec::Vec<i32>) -> f64 + 'static`
              found type `[closure@src/main.rs:27:44: 27:68]`

I thought closures implemented Fn (or FnOnce or FnMut)? Is it not valid to provide a closure when you expect a Fn? I assume I need to specify something else about the Fn in the first line? But what?

I assume I have incorrectly specified the type of either the query: Fn(&Vec<i32>) -> f64 parameter of my first function, or have written the closure wrong |ref fv| my_query(fv, 3).

I read Passing closure to trait method: expected type parameter, found closure, but that seems to be more about passing a closure that isn't the only kind of thing the function is accepting.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
firechant
  • 886
  • 1
  • 14
  • 22
  • [Why is it discouraged to accept a reference to a String (&String) or Vec (&Vec) as a function argument?](https://stackoverflow.com/q/40006219/155423) – Shepmaster Nov 14 '17 at 22:28
  • **Please** read [*The Rust Programming Language*](https://doc.rust-lang.org/stable/book/second-edition/ch13-01-closures.html#using-closures-with-generic-parameters-and-the-fn-traits) which already addresses topics like how to pass closures. – Shepmaster Nov 14 '17 at 22:31

1 Answers1

3

The second error was the more important one:

error[E0277]: the trait bound `for<'r> std::ops::Fn(&'r std::vec::Vec<i32>) -> f64 + 'static: std::marker::Sized` is not satisfied

In short: Fn(&Vec<i32>) -> f64 is not a sized type, and cannot be passed by value. You can only pass trait objects (references or boxes), or values of (sized) types implementing a trait. In this case you probably want the latter:

Playground

fn stat_query<F>(dataset: &Vec<Vec<i32>>, query: F) -> f64
where
    F: Fn(&Vec<i32>) -> f64,
{
    // takes in a dataset of feature vectors and a query,
    // returns the average of the query on the dataset
    if dataset.len() == 0 {
        0.
    } else {
        dataset.iter().map(|ref fv| query(&fv)).sum::<f64>() / (dataset.len() as f64)
    }
}

fn main() {
    // build dataset
    let fv1: Vec<i32> = vec![1, 1, 1, 1, 1];
    let fv2: Vec<i32> = vec![1, 0, 1, 0, 1];
    let my_dataset = vec![fv1, fv2];

    // query checks whether sum of features is greater than a threshold
    fn my_query(ref fv: &Vec<i32>, threshold: i32) -> f64 {
        if fv.iter().sum::<i32>() > threshold {
            1.
        } else {
            0.
        }
    }

    // run query on dataset with threshold 3
    println!("{}", stat_query(&my_dataset, |ref fv| my_query(fv, 3)));
}
Stefan
  • 5,304
  • 2
  • 25
  • 44