[Pre-RFC] a `FnPointer` trait to permit writing more impls

Random thing I wanted to jot down that I was just reminded of. For a long time, we've had this annoyance that you can't implement an impl that is generic over all fn types. This is true for a few reasons: the need for variadic generics is one of them, but also the need to be generic over all ABIs, and the fact that fn(T) cannot equal for<'a> fn(&'a u32) for any type T, as T cannot name the bound region 'a.

A long time I was thinking about adding a fundamental, built-in trait FnPointer that is implemented for all fn types (and for nothing else). Manual impls of this would be illegal, like Sized. It could be as simple as a single method, that converts the fn pointer to a usize. This would allow implementing PartialEq, Eq, Hash, and all those other methods in terms of that method.

In other words, you could write things like:

impl<T: FnPointer> PartialEq for T { ... }

I believe that if FnPointer is a fundamental trait, this would not overlap with other impls of PartialEq and friends. It's not perfect, you couldn't have an impl<T: Foo> PartialEq for T for some other trait. I doubt we have such an impl but maybe we do; I'd have to check the other traits that fn types implement.

I always felt this was too .. idk, hacky or something, but the more I think about it the more I feel like it's an obvious practical sol'n to an annoying set of problems. Has this been proposed and discussed before?

Another option would be something like struct Func<F> { ... } where F: FnPointer. We could then make fn(T) -> B desugar to Func<???> and all zero-sized function types would also desugar to Func<???> (with a unique type inside the Func).

1 Like

If its only method is going to be "convert to usize", maybe it should be implemented for everything that "naturally" points to a code location? I'm thinking function pointers, closures, and function items. This is especially useful for closures, because there is no way on stable to get the "code location" of a closure, which you might want to use for "code equality" or something like that (I recently had a need for this, and gave up and used function pointers).

The alternative might be to prove methods like is_eq, hash, and so forth, which doesn't really effect this question of what it ought to be applied to necessarily.

I mean, I'm mostly interested in building a sandbox that prevents recursion. There's other ways around this, but checking that the same usize value doesn't appear twice is a good-enough approximation (though not quite perfect, since function pointer equality is made of sharp edges and broken dreams[1]).

[1] I hate the PLT.

Related to @Yato's proposal would be a trait for fn pointers and fn items / ZSTs, which I realized could be generalized to closures that have a const inhabitant:

trait FnConst / FnStatic / FnItem   <Args> : Fn<Args> {
    const ITEM: Self;