Skip to content

Allow "ABI agnostic" generics in FFI imports. #2770

@eddyb

Description

@eddyb

For example, a C library might have the following API:

// All examples below assume these two types.
extern "C" {
    type Foo;
    type Bar;
}
extern "C" {
    fn foo_each_bar(
        foo: *mut Foo,
        callback_data: *mut c_void,
        callback: unsafe extern "C" fn(*mut c_void, *mut Bar),
    );
}

However, the c_void pointee makes it harder to use (having to cast to and from *mut c_void) and more error-prone (having no real type safety other than "it's a raw pointer and unsafe to deref").

We could, instead, allow this definition:

extern "C" {
    fn foo_each_bar<T>(
        foo: *mut Foo,
        callback_data: *mut T,
        callback: unsafe extern "C" fn(*mut T, *mut Bar),
    );
}

This is valid because we can fully compute the call ABI for foo_each_bar::<T> without knowing T (and this is something rustc has been able to do independently of LLVM for a while now).
If, e.g. <T> is replaced with <T: ?Sized>, the definition would be disallowed, since the layout of *mut T would then depend on the choice of T, as opposed to always being a pointer scalar.

If that last example works, we can also combine it with Rust references:

extern "C" {
    fn foo_each_bar<T>(
        foo: &Foo,
        callback_data: &mut T,
        callback: extern "C" fn(&mut T, &Bar),
    );
}

Note how the callback no longer needs to be unsafe (since it doesn't take raw pointers)!
An adapter for a closure being used as the callback can be as simple as:

extern "C" fn callback(f: &mut impl FnMut(&Bar), bar: &Bar) {
    f(bar);
}

(or even just |f, bar| f(bar) if we start coercing closures to non-Rust-ABI fn pointers)

cc @rust-lang/compiler

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ffiFFI related proposals.T-langRelevant to the language team, which will review and decide on the RFC.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions