把锈的特性传给C。

时间:2022-06-01 21:58:41

I am building a Rust library that needs to call some C functions with Rust objects. I have a trait with a function that calls the C function, the C function is defined in Rust as follows:

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

extern {
    fn process_trait(my_trait: MyTrait);
}

The idea is that the user can implement the trait for his struct and then call the C functions (basically, the C then calls some other Rust back, which calls some of the Trait functions). The error here is: the trait core::marker::Sized is not implemented for the type Self, because I am passing *self to process_trait. Am I doing something wrong? I tried changing this a bit, even casting, I am getting either this error or wrong type.

其思想是,用户可以为其结构体实现特征,然后调用C函数(基本上,C然后调用其他一些锈回,它调用一些特征函数)。这里的错误是:trait core: marker:: size没有为type Self实现,因为我正在把* Self传递给process_trait。我做错什么了吗?我试着改变一下这个,甚至转换,我得到的不是这个错误就是错误的类型。

I think the problem is that it's supposed to be heap allocated, no? The only thing I am trying to avoid is the API looking ugly. The user should just be able to

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

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

1 个解决方案

#1


2  

Passing a trait object by value doesn't make sense, especially when interacting with C. The actual type (in the C sense) and its size is not known, and there's no vtable inside the object. You most likely want to pass a trait reference (&MyTrait). However, traits are foreign to C and thus make a bad interface. While you could define the equivalent of core::raw::TraitObject in C, actually doing anything with the vtable is extremely ugly and fragile and unsafe.

通过值传递一个trait对象是没有意义的,尤其是在与C交互的时候。真正的类型(在C意义上)和它的大小是未知的,并且对象内部没有vtable。你很可能想传递一个特性引用(&MyTrait)。然而,C语言并不具备这些特性,因此会造成不好的界面。虽然您可以定义core: raw:::TraitObject在C中,实际上对vtable执行任何操作都是极其丑陋、脆弱和不安全的。

If you need polymorphism across the Rust-C barrier, explicit function pointers are a much better option. You can still have MyTrait and the callC method, it's just the FFI parts that look different. It may be possible to hand the C library callbacks with the objects as payload.

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

Alternatively, pass around the aforementioned TraitObjects (fat pointers) but never inspect them from C, call methods through (non-trait) helper function in Rust:

或者,传递前面提到的TraitObjects (fat指针),但从不从C检查它们,调用方法(非特征)helper函数in Rust:

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

This avoids the trouble that is manually digging through a Rust vtable in C.

这避免了在C中手工挖掘铁锈vtable的麻烦。

#1


2  

Passing a trait object by value doesn't make sense, especially when interacting with C. The actual type (in the C sense) and its size is not known, and there's no vtable inside the object. You most likely want to pass a trait reference (&MyTrait). However, traits are foreign to C and thus make a bad interface. While you could define the equivalent of core::raw::TraitObject in C, actually doing anything with the vtable is extremely ugly and fragile and unsafe.

通过值传递一个trait对象是没有意义的,尤其是在与C交互的时候。真正的类型(在C意义上)和它的大小是未知的,并且对象内部没有vtable。你很可能想传递一个特性引用(&MyTrait)。然而,C语言并不具备这些特性,因此会造成不好的界面。虽然您可以定义core: raw:::TraitObject在C中,实际上对vtable执行任何操作都是极其丑陋、脆弱和不安全的。

If you need polymorphism across the Rust-C barrier, explicit function pointers are a much better option. You can still have MyTrait and the callC method, it's just the FFI parts that look different. It may be possible to hand the C library callbacks with the objects as payload.

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

Alternatively, pass around the aforementioned TraitObjects (fat pointers) but never inspect them from C, call methods through (non-trait) helper function in Rust:

或者,传递前面提到的TraitObjects (fat指针),但从不从C检查它们,调用方法(非特征)helper函数in Rust:

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

This avoids the trouble that is manually digging through a Rust vtable in C.

这避免了在C中手工挖掘铁锈vtable的麻烦。