Implementing traits for functions with contrained arguments

Here's a playground link to some code that shows my issue.

I have a type (Foo) that can be constructed From<T>, which - my understanding is - that T::Into<Foo> is automatically implemented for me.

I then create a trait (Function) where the idea is that I can use it to call a fn where the arguments adhere to Foo::Into<Arg> and return value adheres to Foo::From<R> with some context object supplied (in the example it's just a slice) that can take Foo objects and apply them to the function and return a Foo back, where the trait handles all the conversions necessary.

However, no matter what I do I cannot seem to get the compiler to believe that the functions implement the Function trait. Am I missing something or is there an issue related to this that I haven't found yet?

Also, if there's another, better way of doing what I want, I'm open to it. Although I don't want to be using nightly and fn_tuples.

I don't have much experience with function pointers, but this looks almost like a bug to me?

I've edited your playground example in a way it compiles.
See here

edit:
Never mind, conversion during variable assignment tricked me :blush:

Each function has its own type (which implements Fn). test's type is test, not fn(i32) -> i32.

Unfortunately, Fn-family can be implemented multiple times with different Args, so you can't write this:

impl<A: From<Foo>, R: Into<Foo>, F> Function for F
    where
        F: Fn(A) -> R,
{
    fn call(&self, xs: &[Foo]) -> Foo {
        self(xs[0].into()).into()
    }
}

Anyway here's a clunky workaround....

fn wrap_function<'a, A: From<Foo> + 'a, R: Into<Foo> + 'a, F: Fn(A) -> R + 'a>(f: F) -> Box<dyn Function + 'a> {
    struct Wrapper<A, R, F>(F, std::marker::PhantomData<fn(A) -> R>);

    impl<A: From<Foo>, R: Into<Foo>, F> Function for Wrapper<A, R, F>
        where
            F: Fn(A) -> R,
    {
        fn call(&self, xs: &[Foo]) -> Foo {
            (self.0)(xs[0].clone().into()).into()
        }
    }

    Box::new(Wrapper::<A, R, F>(f, std::marker::PhantomData))
}


fn main() {
    let f: Box<dyn Function> = wrap_function(test);
    
    println!("{:?}", f.call(&[Foo(10), Foo(20)]));
}

Of course it's much complicated than just

let f: Box<dyn Function> = Box::new(test as fn(i32) -> i32);

But it save one pointer direction so at least it has some worth.

Thanks!

Okay, but - if I'm understanding the reply - that would also require me making a wrapper struct (and function) for N implementations, where N=number of arguments.

Your example supports a single argument Fn: Fn(A) -> R, but I'd need wrap_function2<'a, A, B, R> for a 2-argument function, and wrap_function3<'a, A, B, C, R> for a 3-argument function, etc.

I don't mind making multiple impl for Function for N arguments (or a macro to do it), and I don't mind wrapping a function, but I was really hoping to avoid having to know to wrap N functions with N different types based on N args, etc.

Is there any way to avoid that?

Yes your understanding is correct.

There's some options.

  1. If you don't care about extreme runtime efficiency, You can just use function pointers with as like I mentioned at the last part. It still requires you spell oit the function signature so though.

  2. If you want to go to the complicated path and don't want multiple wrapper function, here's a way. Playground. You can use a macro to generate most of the code.

Seriously... thanks!

I'll have to study this a bit more, but a few modifications for my own use-case and a little macro, it "just works" now as I was hoping it would.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.