Implement trait for types that implement generic Fn

I have the following construct:

pub trait Foo{
	type Bar;
	fn foobar(&self, args: &[Self::Bar]) -> bool;
}

impl<F> Foo for F
	where F: Fn(u32) -> bool {
	type Bar = u32;
	fn foobar(&self, args: &[Self:Bar]) -> bool {
            (self)(args[0].clone()) // assuming args.len() > 0
	}
}

Now I want to implement Foo for all of Fn(Arg) -> bool, as long as Arg implements Clone.

First attempt:

impl<F, Arg> Foo for F
	where F: Fn(Arg) -> bool, Arg: Clone {
	type Bar = Arg;
	fn foobar(&self, args: &[Self:Bar]) -> bool {
            (self)(args[0].clone()) // assuming args.len() > 0
	}
}

This gives

error[E0207]: the type parameter `Arg` is not constrained by the impl trait, self type, or predicates

Second attempt:

impl<F, Arg> Foo for F
	where F: Fn<Arg>, Arg: Clone {
	type Bar = Arg;
	fn foobar(&self, args: &[Self:Bar]) -> bool {
            (self)(args[0].clone()) // assuming args.len() > 0
	}
}

This gives

error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change. Use parenthetical notation (Fn(Foo, Bar) -> Baz) instead

The three criterias shown in rustc --explain E0207 seem not applicable here:

  • it appears in the implementing type of the impl, e.g. impl<T> Foo<T>
    Reason: We are not implementing a struct.
  • for a trait impl, it appears in the implemented trait, e.g. impl<T> SomeTrait<T> for Foo
    Reason: The trait Foo (in my example) does not accept any type parameter. And I don't really want to add the type parameter to it because it would then require adding the type parameter to any struct that has a field which implements Foo. This would then force the use of PhantomData since the type parameter in the struct has to be used.
  • it is bound as an associated type, e.g. impl<T, U> SomeTrait for T where T: AnotherTrait<AssocType=U>
    Reason: Fn does not have associate type (or I don't know how to add it)

Any clue?

What happens if I have a type that implements both Fn(u8) -> bool and Fn(u16) -> bool? Which implementation of Foo should it get? It can't have both.

You got me. Do you think there are alternatives other than putting type parameter on Foo?

It really depends on the actual use case (what Foo is actually intended to do.) I assume it's a property of predicate closures, since all it requires is a Fn(_) -> bool implementation. I'm not sure there's a better way of doing this with a trait.

It is indeed used as a predicate closure. I'll see what I can do to make the it less messy (not a fan of PhantomData). Thanks a lot!

What you might be able to do, which is also a bit messy but keeps the mess away from users, is to make an intermediate FooImpl wrapper type:

struct FooImpl<Arg, F>
where
    Arg: Clone,
    F: Fn(Arg) -> bool,
{
    closure: F,
    arg: std::marker::PhantomData<fn(Arg)>,
}

impl<Arg, F> Foo for FooImpl<Arg, F>
where
    Arg: Clone,
    F: Fn(Arg) -> bool,
{
    type Bar = Arg;
    fn foobar(&self, args: &[Self::Bar]) -> bool {
        (self.closure)(args[0].clone())
    }
}

fn wrap<Arg: Clone + 'static, F: Fn(Arg) -> bool + 'static>(f: F) -> Box<dyn Foo<Bar = Arg>> {
    Box::new(FooImpl {
        closure: f,
        arg: std::marker::PhantomData,
    })
}

wrap will turn a predicate into a boxed Foo.

Impressive.

One question: why does Arg has to be 'static?

After some research, to my surprise, I found some alternatives. @asymmetrikon Do you mind taking a look and share what you think?

There are two orthogonal choices of the solution:

  1. Choice one: Implement Foo for trait object OR for function pointer
  2. Choice two: Whether or not use associated types

This gives us 4 alternatives but I don't want to make this too long, so I use associated types in the following.

  • Implement Foo for trait object
pub trait Foo{
    type Bar;
    fn foobar(&self, args: &[Self::Bar]) -> bool;
}

impl<T: Clone> Foo for dyn Fn(T) -> bool {
    type Bar = T;
    fn foobar(&self, args: &[Self::Bar]) -> bool {
        (self)(args[0].clone()) // assuming args.len() > 0
    }
}

fn main() {
    let f = (&|x| {true}) as (&dyn Fn(u32) -> bool);
    f.foobar(&[1]);
}
  • Implement Foo for function pointer
impl<T: Clone> Foo for fn(T) -> bool {
    type Bar = T;
    fn foobar(&self, args: &[Self::Bar]) -> bool {
        (self)(args[0].clone())
    }
}
fn main() {
    // Coercion allowed coz. closure does not borrow, move nor capture
    let f: fn(u32) -> bool = |x| {true};
    f.foobar(&[1]);
}

What do you think of the pros and cons of these approaches?