Description
(if there's already an issue for this, please let me know - I've been looking around and I haven't quite seen it.)
In stable, you currently can't access associated consts of a trait in a const fn. I see some discussion in the hidden comments of rust-lang/rust#57563 and in #1 about this problem, but they seem geared towards the idea of having a const trait
. I would like to be able to access associated consts in a non-const trait.
Specifically, what I really want is to be able to get a function pointer from a trait, but not actually call the function. (This seems to me like it's basically equivalent to getting an associated const - it's something statically known at compile time.) And I want that function itself to be non-const. I see some discussion in rust-lang/rust#63997, but again, that seems like it's envisioning passing pointers to other const functions into the const function and calling them. I just want the pointer itself, which is const, and I want to let the function
Both of these work in nightly with #![feature(const_fn)]
, but since the discussion in the above issues is leaning towards const traits and pointers to const fns, I wanted to make sure there was a plan for regular traits and (const) pointers to non-const fns. (Also, it seems to me like this specific case could be stabilized now, though I do see the comments about whether this would imply stabilizing trait bounds without requiring you to specify const impl
.)
The motivating example here is providing plugins to some existing C program via FFI. Usually you'd register these by making some structure with some metadata and some function pointers, like
#[repr(C)]
struct FFIPlugin {
major: u32,
minor: u32,
do_stuff: Option<extern "C" fn()>,
}
This basically perfectly matches Rust's trait concepts, so it'd be nicest to implement this by making a trait:
trait Plugin {
const VERSION: (u32, u32);
fn do_stuff();
}
extern "C" fn do_stuff_callback<T: Plugin>() {
T::do_stuff()
}
const fn make_ffi_plugin<T: Plugin>() -> FFIPlugin {
FFIPlugin {
major: T::VERSION.0,
minor: T::VERSION.1,
do_stuff: Some(do_stuff_callback::<T>),
}
}
You want make_ffi_plugin
to be a const fn so that you can assign it to a static. Sometimes you want this because the interface for loading plugins is to declare a static with a specific name, sometimes you just want this for security hardening (making sure that the resulting FFIPlugin struct is in the read-only section of program memory with a static lifetime and not on the heap):
impl Plugin for MyPlugin {
const VERSION: (u32, u32) = (1, 0);
fn do_stuff() {println!("Hello world");}
}
#[no_mangle]
pub static FFI_PLUGIN: FFIPlugin = make_ffi_plugin::<MyPlugin>();
This works great in nightly Rust (playground link). We use this for Linux kernel ops structs (where the extern "C"
callback function is more complicated than the above examples, and maps appropriately between raw pointers provided by the kernel and safe Rust types in our code).
In stable Rust, it complains "trait bounds other than Sized
on const fn parameters are unstable" and "function pointers in const fn are unstable". I think that's because of the two issues I linked above. But we're not fully using the trait inside the const fn - we're definitely not calling do_stuff
from constant context - nor are we calling any function pointers. We just need references to them. (And we very much need Plugin
to be a non-const trait.)
Would it be possible to stabilize this specific use case? (If this is the sort of thing that wants implementation help, I'd be happy to try!)
Is this resolved by the ?const
syntax from rust-lang/rfcs#2632? It's not clear to me whether const fn make_ffi_plugin<T: ?const Plugin>()
would be allowed to access associated consts + function pointers involving type T.