Create a FunctionPointer
trait that is "fundamental" (in the coherence sense) and built-in to the compiler. It is automatically implemented for all fn
types, regardless of any other details (ABI, argument types, and so forth).
It is not possible to write an impl that is parameteric over all fn types today. This is for a number of a reasons:
We are unlikely to ever make it possible to write an impl generic over all of those things.
And yet, there is a frequent need to write impls that work for any function pointer. For example, it would be nice if all function pointers were Ord
, just as all raw pointers are Ord
.
To work around this, it is common to find a suite of impls that attempts to emulate an impl over all function pointer types. Consider this code from the trace
crate, for example:
trace_acyclic!(<X> fn() -> X);
trace_acyclic!(<A, X> fn(&A) -> X);
trace_acyclic!(<A, X> fn(A) -> X);
trace_acyclic!(<A, B, X> fn(&A, &B) -> X);
trace_acyclic!(<A, B, X> fn(A, &B) -> X);
trace_acyclic!(<A, B, X> fn(&A, B) -> X);
trace_acyclic!(<A, B, X> fn(A, B) -> X);
...
Or this code in the standard library:
As part of the work to remove the leak-check in the compiler, we introduced a warning about potential overlap between impls like
impl<T> Trait for fn(T)
impl<U> Trait for fn(&U)
This is a complex topic. Likely we will ultimately accept those impls as non-overlapping, since wasm-bindgen relies on this pattern, as do numerous other crates – though there may be other limitations. But many of the use cases where those sorts of impls exist would be better handled with an opaque FunctionPointer
trait anyhow, since what they're typically really trying to express is "any function pointer" (wasm-bindgen is actually somewhat different in this regard, as it has a special case for fns that taken references that is distinct from fns that taken ownership).
Add in a trait FunctionPointer
that is implemented for any fn
type (but only fn
types). It is built-in to the compiler, tagged as #[fundamental]
, and does not permit user-defined implementations. It offers a core operation, as_usize
, for converting to a usize
, which in turn can be used to implement the various built-in traits:
#[fundamental]
pub trait FunctionPointer: Copy + Ord + Eq {
fn as_usize(self) -> usize; // but see alternatives below
}
impl<T: FunctionPointer> Ord for T {
}
impl<T: FunctionPointer> PartialEq for T {
fn eq(&self, other: &T) -> bool {
self.as_usize() == other.as_usize()
}
}
impl<T: FunctionPointer> Eq for T { }
In terms of the implementation, this would be integrate into the rustc trait solver, which would know that only fn(_): FunctionPointer
.
As with Sized
, no user-defined impls would be permitted.
#[fundamental]
trait should handle that, but we have to experiment to see how smart the trait checker is.usize
?
dlsym
returns a pointer, so in practice this is a pretty hard requirement.fn
to usize
(or to cast with as
), so to some extent we've already baked in this dependency.dyn Trait
and friends?
fn
types, since there is no way to be generic over all the different sorts of bound regions one might have (e.g., over for<'a> dyn Fn(&'a u32)
and so forth).fn
types, their size is not fixed, so as_usize
could not work, which might argue for the "extended set of operations" approach.&dyn Fn()
for fn()
.DynType
trait would be a good addition.FnDef
types (the unique types for each function)
FunctionPointer
apply to FnDef
types, that can be an ergonomic win and quite useful.as_usize
could trigger us to reify a function pointer.FnDef
is not, in fact, a function pointer, just something that could be used to create a function pointer.FunctionPointer
trait, so that as_usize
and friends can be used from const functionsInstead of the as_usize
method, we might have methods like ord(Self, Self) -> Ordering
that can be uesd to implement the traits. That set can grow over time since no user-defined impls are permitted.
This is obviously less 'minimal' but might work better (as noted above) if we extend to exotic platforms or for dyn
types.
However, it may be that there is extant code that relies on converting fn pointers to usize
and such code could not be converted to use fn
traits.