将 Rust trait 传递给 C

Use*_*ser 5 c ffi rust

我正在构建一个需要使用 Rust 对象调用一些 C 函数的 Rust 库。我有一个调用 C 函数的函数的特征,C 函数在 Rust 中定义如下:

extern {
    fn process_trait(my_trait: MyTrait);
}
Run Code Online (Sandbox Code Playgroud)

这个想法是,用户可以为他的结构实现 trait,然后调用 C 函数(基本上,C 然后调用其他一些 Rust,它调用一些 Trait 函数)。这里的错误是:the trait core::marker::Sized is not implemented for the type Self,因为我正在传递*self给 process_trait。难道我做错了什么?我尝试稍微改变一下,即使是铸造,我得到这个错误或错误的类型。

我认为问题在于它应该是堆分配的,不是吗?我唯一要避免的是 API 看起来很丑。用户应该能够

struct MyUnit;
impl MyTrait for MyUnit...
MyUnit.callC();
Run Code Online (Sandbox Code Playgroud)

小智 2

按值传递特征对象没有意义,尤其是在与 C 交互时。实际类型(在 C 意义上)及其大小未知,并且对象内部没有 vtable。您很可能想要传递特征引用 ( &MyTrait)。然而,特征对于 C 来说是陌生的,因此会产生糟糕的接口。虽然您可以在 C 中定义等效项core::raw::TraitObject,但实际上使用 vtable 执行任何操作都是极其丑陋、脆弱和不安全的。

如果您需要跨越 Rust-C 障碍的多态性,显式函数指针是一个更好的选择。你仍然可以使用MyTrait这个callC方法,只是 FFI 部分看起来不同。可以将对象作为有效负载传递给 C 库回调。

或者,传递上述TraitObjects(胖指针),但永远不要从 C 中检查它们,通过 Rust 中的(非特征)辅助函数调用方法:

extern fn call_method(obj: TraitObject) {
    let obj: &MyTrait = transmute(obj); // should be safe, but not tested
    obj.meth();
}
Run Code Online (Sandbox Code Playgroud)

这避免了在 C 中手动挖掘 Rust vtable 的麻烦。