将Rust特征传递给C

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

extern {
    fn process_trait(my_trait: MyTrait);
}

这个想法是用户可以为他的struct实现trait,然后调用C函数(基本上,C然后调用其他一些Rust,它会调用一些Trait函数).这里的错误是:trait core :: marker :: Sized没有为Self类型实现,因为我将* self传递给process_trait.难道我做错了什么?我尝试改变这一点,甚至施法,我得到这个错误或错误的类型.

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

struct MyUnit;
impl MyTrait for MyUnit...
MyUnit.callC();

最佳答案 按值传递特征对象没有意义,尤其是在与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();
}

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

点赞