一位同事和我一直在讨论如何通过FFI将bool
从<stdbool.h>
(a.k.a。_Bool
)返回Rust。
我们想要在Rust中使用我们的C99代码:
bool
myfunc(void) {
...
}
我们让Rust知道使用myfunc
块的extern C
:
extern "C" {
fn myfunc() -> T;
}
T
应该是什么具体类型?
Rust在c_bool
包中没有libc
,如果你在互联网上搜索,你会发现各种GitHub问题和RFC,供人们讨论这个问题,但不是真的来关于什么是正确和便携的共识:
据我所知:
bool
的大小未定义,但事实上它必须至少足以存储true
(1)和false
(0)。换句话说,至少有一点长。 This comment建议如果将C99 bool
作为参数传递给函数,或者将函数作为返回值传递给函数,和 bool
如果小于C int
,则会将其提升为与int
相同的大小。在这种情况下,我们可以告诉Rust T
是u32
。
好吧,但如果(由于某种原因)C99 bool
是64位宽怎么办? u32
仍然安全吗?也许在这种情况下,我们会截断4个最重要的字节,这很好,因为4个最低有效字节足以表示true
和false
。
我的推理是否正确?在Rust获得libc::c_bool
之前,您会对T
使用什么?为什么C99 bool
(&gt; = 1位)的所有可能大小都安全可移植?
答案 0 :(得分:7)
从2018-02-01开始,Rust的bool大小为officially the same as C's _Bool
。
这意味着bool
是在FFI中使用的正确类型。
在做出正式决定之前,本答案的其余部分适用于Rust的版本
在Rust获得
libc::c_bool
之前,你会对T
使用什么?为什么它对于所有可能大小的C99 bool(&gt; = 1位)都是安全和便携的?
正如您已经链接的那样,官方答案仍然“待定”。这意味着保证正确的唯一可能性是:没有。
这是对的,尽管可能很难过。唯一真正安全的做法是将bool
转换为已知的固定大小的整数类型,例如u8
,以用于FFI。这意味着你需要在双方组织它。
实际上,我会在我的FFI代码中继续使用bool
。正如人们所指出的那样,它神奇地排列在目前广泛使用的所有平台上。如果语言决定使bool
FFI兼容,那么你很高兴。如果他们决定别的东西,如果他们没有引入一个lint来让我们快速发现错误,那么我会非常感到高兴。
另见:
答案 1 :(得分:0)
经过深思熟虑,我将尝试回答我自己的问题。如果您能在以下推理中找到漏洞,请发表评论。
这不是正确答案 - 请参阅以下评论
我认为Rust u8
对T
始终是安全的。
我们知道C99 bool
是一个足以存储0或1的整数,这意味着它可以自由地成为至少1位的无符号整数,或者(如果你感觉很奇怪)签名至少为2位的整数。
让我们按案例分解:
如果C99 bool
是8位,则Rust u8
是完美的。即使在签名的情况下,最高位也将为零,因为代表0和1从不需要2的负幂。
如果C99 bool
大于Rust u8
,那么通过“将其向下”转换为8位大小,我们只会丢弃前导零。因此这也是安全的。
现在考虑C99 bool
小于Rust u8
的情况。从C函数返回值时,由于底层调用约定,不可能返回大小小于一个字节的值。 CC将要求将返回值加载到寄存器或堆栈中的某个位置。由于最小的寄存器或存储器位置是一个字节,因此需要将返回值扩展(使用零)至少一个字节大小的值(我相信函数参数也是如此,它也必须符合调用约定)。如果该值扩展为一个字节的值,则它与情况1相同。如果该值扩展到更大的大小,则它与情况2相同。