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

3

在Rust中进行FFI时,()(或任何其他零大小类型)的等效物是什么?具体而言,当编写extern "C"函数时,我想知道作为函数参数的()的最合理等效物。

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

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

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

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

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

2
只是确认一下:在 C 中,零长度类型是无效的,因此您不能(也不应该)在 C FFI 情况下使用 Rust 的 ZST。 - Boiethios
2个回答

5
在C语言中没有与()等效的东西。有些人可能会认为void是等效的,但实际上并不是这样。当然它们有相似之处,在大多数情况下它们是可以互换的,就像你所说的:pub extern "C" fn test_ffi() -> ()将被正确解释为void test_ffi(void)。(注:这里参数列表中的void表示该函数不接受任何参数,因此它不是一个空类型)。
你可以把void看作是“nothing”,但是()是什么呢?不,它是一个空元组,而void实际上只是“nothing”。

我认为在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中使用任何零大小类型。

谢谢!确认 ZST 在 C FFI 中无效(除了 () 作为返回类型的特殊情况)是我主要想要的。 - randomPoison

1
在C/C++中,“void”有多重含义。它可以表示一个函数不带参数或者没有返回值,也可以是“void*”,指向某些数据但未说明该数据的类型。
对于前一种情况,对于那些没有返回值或不带参数的函数,只需在函数描述中省略它们即可。技术上讲,返回值为空的函数实际上返回“()”,但不需要显式地写出来。在这种情况下,“单元类型”与“void”具有相同的作用,即使单元是“某物”,而不是“无物”。
对于后一种情况,即“void*”,winapi-rs crate定义了一个空枚举c_void,然后使用mut *c_voidconst *c_void作为参数和结构体的类型,这些结构体使用了void指针。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接