创建零大小结构的多种方法有什么区别?

What are the differences between the multiple ways to create zero-sized structs?

我找到了四种不同的方法来创建没有数据的 struct

(我将只包含 ()s 和单变体 enum 声明的任意嵌套元组排除在外,因为我理解为什么不应该使用它们)。

这四种声明有什么区别?我会将它们用于特定目的,还是可以互换?

这本书和参考资料出奇地没有帮助。我确实发现 this accepted RFC (clarified_adt_kinds) 有点不同,即单元结构也声明了一个常量值 D 并且元组结构也声明了构造函数 B()C(_: ()) .但是它没有提供关于为什么使用哪个的设计指南。

我的猜测是,当我使用 pub 导出它们时,实际上可以在我的模块之外构造哪些种类存在差异,但我没有找到关于那个的结论性文档。

struct D; // unit struct

这是人们编写零大小的常用方法 struct

struct A{} // empty struct / empty braced struct
struct B(); // empty tuple struct

这些只是基本 struct 和元组 struct 的特例,它们碰巧没有参数。 RFC 1506 解释允许那些(他们以前不这样做)的理由:

Permit tuple structs and tuple variants with 0 fields. This restriction is artificial and can be lifted trivially. Macro writers dealing with tuple structs/variants will be happy to get rid of this one special case.

因此,它们很容易由宏生成,但很少有人会自己编写。

struct C(()); // unit-valued tuple struct

这是元组的另一个特例struct。在 Rust 中,() 是一种与任何其他类型一样的类型,因此 struct C(());struct E(u32); 没有太大区别。虽然类型本身不是很有用,但禁止它会产生另一个需要在宏或泛型中处理的特殊情况(struct F<T>(T) 当然可以实例化为 F<()>)。

请注意,在 Rust 中还有许多其他方法可以拥有空类型。例如。可以有一个函数 return Result<(), !> 来指示它不产生值,并且不会失败。虽然您可能认为 returning () 在这种情况下会更好,但如果您实现了一个指示您 return Result<T, E> 但是让你选择 T = ()E = !.

这四个定义之间只有两个功能差异(我稍后会提到第五种可能性):

  1. 语法(最明显)。 更详细。
  2. 当结构被标记为 pub 时,它的 constructor (also called struct literal syntax) 是否可以在定义它的模块之外使用。

您的示例中唯一不能从当前模块外部直接构造的示例是 C。如果你尝试这样做,你会得到一个错误:

mod stuff {
    pub struct C(());
}
let _c = stuff::C(());  // error[E0603]: tuple struct `C` is private

出现这种情况是因为该字段没有被标记pub;如果将 C 声明为 pub struct C(pub ()),则错误消失。

还有另一种你没有提到的可能性,它给出了一个稍微更具描述性的错误信息:一个普通的结构,有一个零大小的非 pub 成员。

mod stuff {
    pub struct E {
        _dummy: (),
    }
}
let _e = stuff::E { _dummy: () };  // error[E0451]: field `_dummy` of struct `main::stuff::E` is private

(同样,您可以通过使用 pub 声明它来使 _dummy 字段在模块外部可用。)

由于 E 的构造函数只能在 stuff 模块内部使用,因此 stuff 可以独占控制 E 的值何时以及如何创建。标准库中的许多结构都利用了这一点,比如 Box(举一个明显的例子)。零大小类型的工作方式完全相同;事实上,从它定义的模块外部,您知道不透明类型是零大小的唯一方法是调用 mem::size_of.

另见