1

Is it possible to generate and store a closure in a struct: currently getting the error mismatched types

pub struct MM<F>
where
    F: Fn(&f64, &f64) -> f64,
{
    pub n: f64, //
    pub y: f64, //
    p: f64,     // the p parameter associated with prior probability
    f: F,       // this must only take two arguments
}

impl<F> MM<F>
where
    F: Fn(&f64, &f64) -> f64,
{
    pub fn new(l: f64, prior: f64) -> Self {
        let func = |a: &f64, b: &f64| -> f64 { b.powf(prior) * a };
        MM {
            n: l,
            y: l,
            p: prior,
            f: func,
        }
    }
}
JimGreen
  • 15
  • 4

1 Answers1

1

I've made some adjustments to the code you've posted. Mainly I've changed the return type of new (see explanation here as to why this is necessary). I've also changed CFMM to MM in the struct creation of new, it seems like that was a typo.

Short version for the impatient: you can't return Self from new and put a closure in it. The compiler will tell you that

every closure has a distinct type and so could not always match the caller-chosen type of parameter F

if you attempt to do this. The key element here is "caller-chosen". Rust cannot guarantee that the closure we're constructing in new will match whatever the caller decides F should be. As soon as you make new decide explicitly what it will return impl Fn(...) it works.

There's some more explanation available here as well.

pub struct MM<F: Fn(&f64, &f64) -> f64> {
    pub n: f64,
    pub y: f64,
    pub p: f64,     // the p parameter associated with prior probability
    pub f: F,       // this must only take two arguments
}

pub fn new_mm(l: f64, prior: f64) -> MM<impl Fn(&f64, &f64) -> f64> {
    let func = move |a: &f64, b: &f64| -> f64 { b.powf(prior) * a };
    MM {
        n: l,
        y: l,
        p: prior,
        f: func,
    }
}

fn main() {
    let a: f64 = 1.0;
    let b: f64 = 0.9;
    let p  = new_mm(a, b);
    println!("{}", (p.f)(&1.0, &2.0));
}

Playground

fresskoma
  • 25,481
  • 10
  • 85
  • 128
  • Thanks: this removes the error in the struct itself but it doesn't seem possible to actually initialiase using ::new in main as it doesn't seem to either find the type parameters, or like them not being declared. ```rust fn main() { let a = 1.0; let b = 0.9; let p:CFMM = {CFMM::new(a, b)}; } ``` – JimGreen Feb 20 '23 at 18:23
  • 1
    You're right. My Rust-foo is not strong enough to solve this while keeping `new` as an associated function to the struct itself. I think this may be a chicken-egg problem, so I'm not 100% sure it is solvable. In any case, if you move `new` out of the struct it works fine :) I've updated the answer to reflect that. – fresskoma Feb 20 '23 at 22:50