2.3. 其他repr
原文链接:doc.rust-lang.org/nomicon/other-re...
可选的数据表达方式#
Rust 允许你选择其他的数据布局策略。
repr(C)#
这是最重要的一种 repr
。它的目的很简单,就是和 C 保持一致。数据的顺序、大小、对齐方式都和你在 C 或 C++ 中见到的一摸一样。所有你需要通过 FFI 交互的类型都应该有 repr(C)
,因为 C 是程序设计领域的世界语。而且如果我们要在数据布局方面玩一些花活的话,比如把数据重新解析成另一种类型,repr(C)
也是很有必要的。
但是,一定不要忘了 Rust 的那几个奇行种。repr(C)
的存在有双重作用,既为了 FFI 同时也为了常规的布局控制,所以它可以被应用于那些在 FFI 中没有意义甚至会产生错误的类型。
- 尽管标准的 C 语言不支持大小为 0 的类型,但 ZST 的尺寸仍然是 0。而且它也与 C++ 中的空类型有着明显的不同,C++ 的空类型还是要占用一个字节的空间的。
- DST 的指针(胖指针),元组,和带有成员变量的枚举都是 C 中没有的,因此也不是 FFI 安全的。
- 如果
T
是一个 FFI 安全的非空指针 , 那么Option<T>
可以保证和T
拥有相同的布局和 ABI,当然它也会是 FFI 安全的。这一规则适用于&
,&mut
和函数指针等所有非空的指针。 - 在
repr(C)
中元组结构体与结构体基本相同,唯一的不同是其成员都是未命名的。 - 对于枚举的处理和
repr(u*)
是相同的(见下一节)。选择的类型尺寸等于目标平台上 C 的应用二进制接口 (ABI) 的默认枚举尺寸。注意 C 中枚举的数据布局是确定的,所以这确实是一种 “最合理的假设”。不过,当目标 C 代码编译时加了一些特殊的编译器参数时,这一点可能就不正确了。 repr(C)
和repr(u*)
中无成员的枚举不能被赋值为一个没有对应变量的整数,尽管在 C\C++ 中这是一种合法的行为。构建一个没有对应变量的枚举类型实例属于未定义行为。(对于存在准确匹配的值是允许正常编写和编译的)
repr(u), repr(i)#
这两个可以指定无成员枚举的大小。如果枚举变量对应的整数值对于设定的大小越界了,将产生一个编译期错误。你可以手工设置越界的元素为 0 以避免编译错误,不过要注意 Rust 是不允许一个枚举中的两个变量拥有相同的值的。
“无成员枚举” 的意思是枚举的每一个变量里都不关联数据。不指定 repr(u*)
或 repr(i*)
的无成员枚举依然是一个 Rust 的合法原生类型,它们都没有固定的 ABI 表示方法。给它们指定 repr
使其有了固定的类型大小,方便在 ABI 中使用。
Rust 中所有有成员的枚举都没有确定的 ABI 表示方式(即使关联的数据只是 PhantomData
或者零尺寸类型的数据)。
为枚举显式指定 repr
后空指针优化将不再起作用。
这些 repr
对于结构体无效。
repr(packed)#
repr(packed)
强制 Rust 不填充空数据,各个类型的数据紧密排列。这样有助于提升内存的使用效率,但很可能会导致其他的副作用。
尤其是大部分平台都强烈建议数据对齐。这意味着加载未对齐的数据会很低效(x86),甚至是错误的 (一些 ARM 芯片)。像直接加载或存储打包的 (packed) 成员变量这种简单的场景,编译器可能可以用 shift 和 mask 等方式隐藏对齐问题。但是如果是使用一个打包的变量的引用,编译器很可能没办法避免未对齐加载问题。
repr(packed)
不应该随便使用。只有在你有一些极端的需求的情况下才该用它。
这个 repr 是 repr(C)
和 repr(Rust)
的修饰器。