1

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Cooki3Tube
  • 107
  • 1
  • 10
  • Note that every time `test` is called, it is expected that `V` can be instantiated to _one_ type. You would like to move the universal quantification of `V` to `Fn`. Unfortunately, that isn't directly possible. Maybe [this will help?](http://stackoverflow.com/questions/37606035/pass-generic-function-as-argument) – Alec Mar 28 '17 at 18:25
  • This wouldn't solve my problem since I want to pass in several diffrent functions. A Trait can't help either, because the generics would have to be speciefied on a method. Therefore I can't make it into an Object... Is there no way to avoid the monomorphization in this context ? – Cooki3Tube Mar 28 '17 at 18:45

2 Answers2

4

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Thought so too, just wanted to be sure. It needed to be a trait object since I need this closure to be `Sized` – Cooki3Tube Mar 28 '17 at 19:02
  • @Cooki3Tube `f` would be `Sized` in the last code block I show (otherwise it couldn't be a parameter!); so I must be misunderstanding you. – Shepmaster Mar 28 '17 at 19:05
  • I tried this an hour ago and the compiler complained... Now I tried this again and the compiler doesn't complain about this anymore. Strange – Cooki3Tube Mar 28 '17 at 19:09
  • Do you have any advice how I could do this? I just need some logic to be given as a colsure which can operate on S1 and S2 – Cooki3Tube Mar 28 '17 at 19:11
  • @Cooki3Tube you'd have to expand on your question a bit more. The code in both answers shows calling the closure with both `S1` and `S2`. – Shepmaster Mar 28 '17 at 19:15
  • Okay the less simplified version is: I want to be able to do something like the following [link](https://play.rust-lang.org/?gist=ef8cbfa01aae4cbd82b0ecab39b4c96e&version=stable&backtrace=0) I explain my problem there in a bit more detail – Cooki3Tube Mar 28 '17 at 19:33
1

This may not be exactly what you want, but it works:

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

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

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

This causes foo and bar to be cast to a Trait object, which means that it will carry a vtable pointer (may not be desirable). It makes the F a polymorphic call.

Without more information about your types, it's hard to understand what you're trying to do.

bluejekyll
  • 155
  • 2
  • 8