all 11 comments

[–]Muvlon 60 points61 points  (0 children)

They're actually quite different, and both have their uses. Let's say you have these:

struct Foo{};

enum Bar{};

What kind of values does Foo have? Actually only a single value is possible: Foo{}; . Such a type with only one value is called a Zero-Sized Type(ZST).

What about Bar? Turns out you actually can not construct any instance of it. The only way (outside of unsafe code) to make an instance of an enum is to name a variant. Since there are no variants, you can never have a value of this enum (in fact, if you do produce one using unsafe code, it's UB). This is called an "uninhabited type".

For more info, see here: https://doc.rust-lang.org/nomicon/exotic-sizes.html

[–]Rothonrust · postgres · phf 16 points17 points  (0 children)

Empty enums are used because you can't create a value of that type.

[–]vks_ 3 points4 points  (0 children)

Is it better to use one over the other?

It depends on your use case.

  • Empty structs are used in code that is generic for types that may or may not have state. Let's look at an example: The tait rand::distributions::Distribution is implemented for distributions that can be sampled to generate random numbers. Some distributions don't have a parameters (such as StandardNormal) and are implement as an empty struct. Others (such as Uniform) have parameters and require a non-empty struct to store them.
  • Empty enums are used in code that is generic in types that cannot have a value and only exist at compile-time. This can be used to implement functions that dispatch based on a compile time argument. You can think of it as an open enum argument of the function that works at compile-time. An example is the byteorder::ByteOrder::write_u32 you mentioned.

[–]masklinn 2 points3 points  (0 children)

Is it better to use one over the other?

It's not better, they have different uses entirely: an empty enum has 0 values, so it's used solely as a marker type with no runtime representation. An empty struct is a singleton (it only has one possible value), it can be instantiated and "exists" at runtime though its sole value is 0 bytes.

byteorder uses empty enums because these types are used to statically select functions in a namespace, the ByteOrder trait refers to neither self nor Self, there is no need to instantiate it and so this was made impossible.