I found four different ways to create a struct with no data:
struct A{} // empty struct / empty braced structstruct B(); // empty tuple structstruct C(()); // unit-valued tuple structstruct D; // unit struct
(I'm leaving arbitrarily nested tuples that contain only ()s and single-variant enum declarations out of the question, as I understand why those shouldn't be used).
What are the differences between these four declarations? Would I use them for specific purposes, or are they interchangeable?
The book and the reference were surprisingly unhelpful. I did find this accepted RFC (clarified_adt_kinds) which goes into the differences a bit, namely that the unit struct also declares a constant value D and that the tuple structs also declare constructors B() and C(_: ()). However it doesn't offer a design guideline on why to use which.
My guess would be that when I export them with pub, there are differences in which kinds can actually be constructed outside of my module, but I found no conclusive documentation about that.
There are only two functional differences between these four definitions (and a fifth possibility I'll mention in a minute):
pub, whether its constructor (also calledstructliteral syntax) is usable outside the module it's defined in.The only one of your examples that is not directly constructible from outside the current module is
C. If you try to do this, you will get an error:This happens because the field is not marked
pub; if you declareCaspub struct C(pub ()), the error goes away.There's another possibility you didn't mention that gives a marginally more descriptive error message: a normal struct, with a zero-sized non-
pubmember.(Again, you can make the
_dummyfield available outside of the module by declaring it withpub.)Since
E's constructor is only usable inside thestuffmodule,stuffhas exclusive control over when and how values ofEare created. Many structs in the standard library take advantage of this, likeBox(to take an obvious example). Zero-sized types work in exactly the same way; in fact, from outside the module it's defined in, the only way you would know that an opaque type is zero-sized is by callingmem::size_of.See also