This works as expected, but I don't really like the use of mutable static.
I know that every rust_callback will be static and known at compile time.
I was wondering why something like this isn't possible/allowed:
As expected this doesn't compile: error: using function pointers as const generic parameters is forbidden. So the question is why this is forbidden? Is this a compiler limitation or no one thought about feature like this?
Using function pointers for const generics has a very sneaky problem: it's not guaranteed that you get the same exact pointer when you create a function pointer for a specific function item multiple times, or that different function items have different addresses. This is most easily observed by turning a generic function into a function pointer from two different crates.
If the FFI callback provides plumbing for some void* userdata, you can use that to get the extra data through, either the function pointer or a Box<Box<dyn Fn()>> or similar. If it doesn't, you'll basically need to define a custom trait for the callback, e.g.
And as expected I cannot use equality checks in const fn:
const fn equality_check(a: fn(usize), b: fn(usize)) -> bool {
a == b
}
I get a compiler error:
error: pointers cannot be reliably compared during const eval
Now, I'm struggling to understand why fn pointers are allowed in const fns but not in const generics, even if there's a check in the compiler for using the pointers in a wrong way (ex. equality checking). Why can't this mechanism be used for const generics?
Please correct me if I got something wrong here.
Because const generics are constantly and trivially compared for equality, e.g. if you say
let val: Foo<{rust_callback}> = Foo::<{rust_callback}>::new();
this has a requirement for soundness that the two const generics are equivalent. Even more importantly, if the const generics appear in the API of a crate, e.g.
pub fn takes(val: Foo<{rust_callback}>);
then it's absolutely imperative that any crate upon seeing this comes to the exact same equivalent understanding of what types are involved here.
This is potentially resolvable by using symbol identity instead of function pointer identity, but doing so for const generics is far from trivial since you're manipulating the function pointer, not the symbol reference. And we have a functionality for the latter already: type generics and F: Fn(). The only bit missing is some way to bound the type by "is a function item" so it's possible/safe to conjure it up from nothing again.
Now I understand why this works that way. I will mark CAD97 answer (with trait) as general solution to the problem. It's more elegant than using statics anyway.