我找到了四种不同方法来创建没有数据的struct
:
struct A{} // empty struct / empty braced struct
struct B(); // empty tuple struct
struct C(()); // unit-valued tuple struct
struct D; // unit struct
(我离开了任意嵌套的元组,其中只包含()
个和单变量enum
声明,因为我理解为什么不应该使用这些元组。
这四个声明之间有什么区别?我会将它们用于特定目的,还是可以互换?
这本书和参考文章令人惊讶地无益。我找到了this accepted RFC (clarified_adt_kinds),它稍微讨论了差异,即单元结构也声明了一个常量值D
,并且元组结构也声明了构造函数B()
和C(_: ())
。但是,它没有提供关于使用哪个的设计指南。
我的猜测是,当我用pub
导出它们时,实际上可以在我的模块之外构造哪些类型存在差异,但我没有找到关于它的确凿文档。
答案 0 :(得分:4)
这四个定义之间只有两个功能差异(我将在一分钟内提到第五种可能性):
pub
时,其constructor(也称为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
(同样,您可以通过使用_dummy
声明pub
字段来使E
字段在模块外部可用。)
由于stuff
的构造函数仅在stuff
模块中可用,因此E
可以独占控制Box
的值的创建时间和方式。标准库中的许多结构都利用了这一点,例如%module coverage
%{
//#include "../SeqMCMC/src/funeval_base.hpp"
#include "coverage.hpp"
%}
%include "std_vector.i"
void coverage(std::vector<double> *Pypar, std::vector<std::vector<double> > *Pyxpass, std::vector<std::vector<double> > *Pyypass);
(举一个明显的例子)。零大小的类型以完全相同的方式工作;事实上,从它定义的模块外部,你知道opaque类型为零的唯一方法是调用mem::size_of
。
答案 1 :(得分:3)
struct D; // unit struct
这是人们编写零大小struct
的常用方法。
struct A{} // empty struct / empty braced struct
struct B(); // empty tuple struct
这些只是基本struct
和元组struct
的特殊情况,它们恰好没有参数。 RFC 1506解释允许那些(他们不习惯)的理性:
允许使用0字段的元组结构和元组变体。这种限制是人为的,可以轻易解除。处理元组结构/变体的宏编写者将乐于摆脱这一特殊情况。
因此,它们可以很容易地由宏生成,但人们很少会自己编写它们。
struct C(()); // unit-valued tuple struct
这是元组struct
的另一个特例。在Rust中,()
类似于任何其他类型,因此struct C(());
与struct E(u32);
没有太大区别。虽然类型本身不是很有用,但禁止它会使另一个需要在宏或泛型中处理的特殊情况(struct F<T>(T)
当然可以实例化为F<()>
)。
请注意,在Rust中还有许多其他方法可以使用空类型。例如。可以使函数返回Result<(), !>
以指示它不会产生值,并且不会失败。虽然您可能认为在这种情况下返回()
会更好,但是如果您实现了一个要求您返回Result<T, E>
但又可以选择的特征,那么您可以 来执行此操作T = ()
和E = !
。