通过FFI将C99`bool`返回Rust的正确类型是什么?

时间:2017-12-07 22:45:52

标签: type-conversion rust boolean ffi

一位同事和我一直在讨论如何通过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,供人们讨论这个问题,但不是真的来关于什么是正确和便携的共识:

据我所知:

  • C99中bool的大小未定义,但事实上它必须至少足以存储true(1)和false(0)。换句话说,至少有一点长。
  • 甚至可能是one bit wide
  • 其大小可能为ABI defined

This comment建议如果将C99 bool作为参数传递给函数,或者将函数作为返回值传递给函数, bool如果小于C int,则会将其提升为与int相同的大小。在这种情况下,我们可以告诉Rust Tu32

好吧,但如果(由于某种原因)C99 bool是64位宽怎么办? u32仍然安全吗?也许在这种情况下,我们会截断4个最重要的字节,这很好,因为4个最低有效字节足以表示truefalse

我的推理是否正确?在Rust获得libc::c_bool之前,您会对T使用什么?为什么C99 bool(&gt; = 1位)的所有可能大小都安全可移植?

2 个答案:

答案 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 u8T始终是安全的。

我们知道C99 bool是一个足以存储0或1的整数,这意味着它可以自由地成为至少1位的无符号整数,或者(如果你感觉很奇怪)签名至少为2位的整数。

让我们按案例分解:

  1. 如果C99 bool是8位,则Rust u8是完美的。即使在签名的情况下,最高位也将为零,因为代表0和1从不需要2的负幂。

  2. 如果C99 bool大于Rust u8,那么通过“将其向下”转换为8位大小,我们只会丢弃前导零。因此这也是安全的。

  3. 现在考虑C99 bool小于Rust u8的情况。从C函数返回值时,由于底层调用约定,不可能返回大小小于一个字节的值。 CC将要求将返回值加载到寄存器或堆栈中的某个位置。由于最小的寄存器或存储器位置是一个字节,因此需要将返回值扩展(使用零)至少一个字节大小的值(我相信函数参数也是如此,它也必须符合调用约定)。如果该值扩展为一个字节的值,则它与情况1相同。如果该值扩展到更大的大小,则它与情况2相同。