在 FFI 中使用 ()(和其他零大小类型)



在 Rust 中做 FFI 时,相当于()(或任何其他零大小类型(是什么?具体来说,我很好奇在编写extern "C"函数时,()作为函数参数的最合理等价物是什么。

我的理解是零大小类型在 C 中无效,但 Rust 似乎允许它们在extern "C"函数中,例如:

#[no_mangle]
pub extern "C" fn test_ffi(input: ()) -> () {
}

在这种情况下,返回()与在 C# 中声明void函数相同。但是,我不清楚在从 C 生成绑定时如何声明input参数。我的印象是 ZST 在 C 语言中不可表示,因此不应该是 FFI 安全的。似乎nomicon证实了这一点,他说:

为了避免在 FFI 中使用()发出警告,我们改用空数组 ([u8; 0](,它与空类型一样有效,但与 FFI 兼容。

这似乎暗示()与 FFI 不兼容,但[u8; 0]兼容(即使我希望它也是零大小(?

在 C 中没有等价物与()等价物。有些人可能会争辩说void是等价的,但事实并非如此。 当然,它们有相似之处,在大多数情况下,它们是可以互换的,就像你说的:pub extern "C" fn test_ffi() -> ()会被正确地解释为void test_ffi(void).(注意:这里参数列表中的void意味着函数不带参数,因此它不是空类型(。

你可以把void成什么都没有,但()什么都不是吗?不,它是一个空元组,而void实际上什么都没有。

我的印象是 ZST 在 C 语言中不可表示,因此不应该是 FFI 安全的。

不,它们在 C 中是不可表示的:pub extern "C" fn test_ffi(input: (), foo: i32) -> ()这里不清楚 Rust 编译器应该理解什么,因为void test_ffi(void, int32_t foo);在 C 中是无效的。

Nomicon使用空数组使类型不透明。我不建议这样做,但对于这个特定的用例来说可能是可以的。无论如何,C 中的不透明类型是邪恶的。请注意,空数组在 C 语言中是非法的,因此它们只应在 Rust 端使用。

我建议永远不要在任何 FFI 中使用任何零大小的类型。

Void 在 C/C++ 中具有重载的含义。它可以意味着一个函数不带参数或不返回任何内容,或者它可以是一个void*,一个指向某些数据的指针,但没有说明该数据是什么。

对于前一种情况,对于不返回任何参数或不带参数的函数,只需在函数描述中省略它们即可。一个不返回任何内容的函数在技术上是返回(),但它不需要显式编写。在这种情况下,单位类型void用途相同,即使单位是某物而不是虚无

对于void*的后一种情况,winapi-rs crate 将一个c_void定义为空枚举,然后使用mut *c_voidconst *c_void作为使用空指针的参数和结构的类型。

最新更新