在 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_void
或const *c_void
作为使用空指针的参数和结构的类型。