Search code examples
genericsrusttraitsgeneric-function

Call generic closure with concrete type


Is it possible to call a generic function using a concrete type, with a concrete struct?

Here a small example of what I want to do:

trait T {}
trait T2 {}
struct S1 {}
struct S2 {}
impl T for S1 {}
impl T for S2 {}

fn test<V: T>(foo: &S1, bar: &S2, f: &Fn(&V)) {
    f::<S1>(foo);
    f(bar);
}

fn main() {
    test(&S1{}, &S2{}, &|_| println!("called"));
}

I can't get rid of the generic parameter V because the actual problem I want to solve is much more involved. Furthermore I can't create a wrapping trait, because sometimes I have got a weak signature and sometimes f requires many more traits.


Solution

  • This is not possible:

    fn test<V: T>(foo: &S1, bar: &S2, f: &Fn(&V)) {
        f::<S1>(foo);
    }
    

    In effect, the function signature says:

    • Please give me an f that takes a trait object of the trait Fn which takes a reference to a V
    • The caller is allowed to pick any concrete type for V, so long as it implements the trait T

    Then the body of the function says:

    • HAHAHA, just kidding, I'm going to call that f with the concrete type S1, regardless of what the caller picked.

    This cannot work because the V that the caller picked might not be S1. In fact, it's most likely not going to be S1.

    About the only suggestion I can offer is to accept another trait object as the argument:

    fn test(foo: &S1, bar: &S2, f: &Fn(&T)) {
        f(foo);
        f(bar);
    }
    

    but you've already ruled that out, so...


    As a side note, I'm not sure why you are taking the closure as a trait object. Normally, I'd just accept a generic:

    fn test<F>(foo: &S1, bar: &S2, f: F)
        where F: Fn(&T),
    

    This allows for some level of monomorphization to occur.