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

时间:2018-05-03 19:31:21

标签: struct rust encapsulation unit-type

我找到了四种不同方法来创建没有数据的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导出它们时,实际上可以在我的模块之外构造哪些类型存在差异,但我没有找到关于它的确凿文档。

2 个答案:

答案 0 :(得分:4)

这四个定义之间只有两个功能差异(我将在一分钟内提到第五种可能性):

  1. 语法(最明显的)。 mcarton's answer详细介绍。
  2. 当结构标记为pub时,其constructor(也称为struct literal syntax)是否可在其定义的模块之外使用。
  3. 您的示例中唯一一个不能从当前模块外部直接构造的示例是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 = !