Home > OS >  Implement factory function that returns a struct with closure
Implement factory function that returns a struct with closure

Time:08-03

Assume we have a struct StoresFnMut that stores a closure with the help of a generic parameter. It should be stored via a generic type field in order to ensure static dispatch. The stored closure is usually quite simple (e.g., a single number addition), but called very often. So dynamic dispatch would have quite a performance impact.

struct StoresFnMut<F: FnMut(i32) -> i32> {
    pub func: F,
}

Now we want a factory function that returns a concrete instance of StoresFnMut, which combines the struct with some kind of default closure:

fn with_concrete_fn<F: FnMut(i32) -> i32>(vec: &[i32]) -> StoresFnMut<F> {
    StoresFnMut {
        func: |x| vec.get(x),
    }
}

#[test]
fn test() {
    let v = vec![0, 0, 0];
    let _ = with_concrete_fn(&v);
}

However, it fails to compile, and the compiler gives the help that

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

I understand the problem, have read quite some questions around this topic and know some workarounds like for example storing the closure in StoresFnMut in a Box<dyn FnMut>. However, I really want to have it with static dispatch in StoresFnMut. In all the related questions on SO

What would be a possible approach to implement a factory function that returns a concrete instance of StoresFnMut with a default closure?

CodePudding user response:

The problem as you saw is that the way you've declared it, the caller gets to choose the generic type, not the function. If instead your function decides the type it should work ok.

// Note i32 changed to usize as you're using it to index an array
struct StoresFnMut<F: FnMut(usize) -> i32> {
    pub func: F,
}

// Returning 'a type that implements the right trait' instead of a caller
// defined type. Note the addition of the lifetime as there needs to be
// something tying it to the lifetime of the closure
fn with_concrete_fn(vec: &[i32]) -> StoresFnMut<impl FnMut(usize) -> i32   '_> {
    StoresFnMut {
        // indexing instead of `get` so it doesn't return an option
        func: |x| vec[x],
    }
}

#[test]
fn test() {
    let v = vec![0, 0, 0];
    let _ = with_concrete_fn(&v);
}
  • Related