在 FFI 中使用 ()(和其他大小为零的类型)
Using () (and other zero-sized types) in FFI
在 Rust 中执行 FFI 时,()
(或任何其他 zero-sized type)的等价物是什么?具体来说,我很好奇在编写 extern "C"
函数时,()
最合理的等价物作为函数参数是什么。
我的理解是零大小类型在 C 中无效,但 Rust 似乎允许它们在 extern "C"
函数中使用,例如:
#[no_mangle]
pub extern "C" fn test_ffi(input: ()) -> () {
}
在这种情况下,返回 ()
与在 C# 中声明 void
函数相同。但是,我不清楚在从 C 生成绑定时如何声明 input
参数。我的印象是 ZST 在 C 中不可表示,因此不应该是 FFI 安全的。好像是nomicon confirms this,说的是:
And to avoid warning around using ()
in FFI, we instead use an empty array ([u8; 0]
), which works just as well as an empty type but is FFI-compatible.
这似乎暗示 ()
不兼容 FFI,但 [u8; 0]
兼容(即使我希望它也是零大小)?
在 C 中没有 ()
的等价物。有些人可能会争辩说 void
是等价的,但事实并非如此。当然它们有相似之处,在大多数情况下它们是可以互换的,就像你说的:pub extern "C" fn test_ffi() -> ()
将被正确地解释为 void test_ffi(void)
。 (注意:参数列表中的 void
表示该函数不带参数,因此它不是空类型)。
你可以把void
想成什么都不是,但是()
什么都不是吗?不,它是一个空元组,而 void
实际上什么都不是。
I was under the impression that ZSTs aren't representable in C, and so shouldn't be FFI-safe.
不,它们在 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++ 中具有重载含义。它可能意味着一个函数没有参数或 return 什么都没有,或者它可以是一个 void*
,一个指向某些数据但没有说明数据是什么的指针。
对于前一种情况,对于 return 什么也不带参数的函数,只需在函数描述中省略它们即可。 returns nothing 的函数在技术上是 returning ()
,但不需要显式编写。在这种情况下,单位类型 与 void
的用途相同,即使单位是 something 而不是 nothing.
对于 void*
的后一种情况,winapi-rs crate defines a c_void 是一个空枚举,然后使用 mut *c_void
或 const *c_void
作为使用的参数和结构的类型空指针。
在 Rust 中执行 FFI 时,()
(或任何其他 zero-sized type)的等价物是什么?具体来说,我很好奇在编写 extern "C"
函数时,()
最合理的等价物作为函数参数是什么。
我的理解是零大小类型在 C 中无效,但 Rust 似乎允许它们在 extern "C"
函数中使用,例如:
#[no_mangle]
pub extern "C" fn test_ffi(input: ()) -> () {
}
在这种情况下,返回 ()
与在 C# 中声明 void
函数相同。但是,我不清楚在从 C 生成绑定时如何声明 input
参数。我的印象是 ZST 在 C 中不可表示,因此不应该是 FFI 安全的。好像是nomicon confirms this,说的是:
And to avoid warning around using
()
in FFI, we instead use an empty array ([u8; 0]
), which works just as well as an empty type but is FFI-compatible.
这似乎暗示 ()
不兼容 FFI,但 [u8; 0]
兼容(即使我希望它也是零大小)?
在 C 中没有 ()
的等价物。有些人可能会争辩说 void
是等价的,但事实并非如此。当然它们有相似之处,在大多数情况下它们是可以互换的,就像你说的:pub extern "C" fn test_ffi() -> ()
将被正确地解释为 void test_ffi(void)
。 (注意:参数列表中的 void
表示该函数不带参数,因此它不是空类型)。
你可以把void
想成什么都不是,但是()
什么都不是吗?不,它是一个空元组,而 void
实际上什么都不是。
I was under the impression that ZSTs aren't representable in C, and so shouldn't be FFI-safe.
不,它们在 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++ 中具有重载含义。它可能意味着一个函数没有参数或 return 什么都没有,或者它可以是一个 void*
,一个指向某些数据但没有说明数据是什么的指针。
对于前一种情况,对于 return 什么也不带参数的函数,只需在函数描述中省略它们即可。 returns nothing 的函数在技术上是 returning ()
,但不需要显式编写。在这种情况下,单位类型 与 void
的用途相同,即使单位是 something 而不是 nothing.
对于 void*
的后一种情况,winapi-rs crate defines a c_void 是一个空枚举,然后使用 mut *c_void
或 const *c_void
作为使用的参数和结构的类型空指针。