Questions on zero sized types

I have some questions on zero sized types, primarily about syntaxes. Let me show some code below:

struct SNah;

struct SEmpty {}

struct SEmptyTuple();

struct STuple(i32);

fn main() {
    // ok
    let s_nah = SNah;

    // ok
    let s_nah = SNah {};

    // not ok, must include `{}` to compile
    let s_empty = SEmpty;

    // ok
    let s_empty = SEmpty {};
    
    // ok... but is actually a fn() -> SEmptyTuple
    let s_empty_tuple = SEmptyTuple;

    // call the function to get a concrete variable with type SEmptyTuple
    let s_empty_tuple_real = s_empty_tuple();

    // another try for a normal tuple struct
    // s_tuple is a fn(i32) -> STuple, i.e. the plain c`tor in a function type
    let s_tuple = STuple;
    // call the function to get a concrete variable
    let s_tuple_real = s_tuple(42);
    // typical way to construct the tuple
    let s_tuple_real = STuple(1024);
}

My questions:

  1. SNah and SEempty seem to have identical functionalities. If so, why allowing two declaring syntaxes on empty structs?
  2. Continuing 1., why the nuances on declaring variables (the optional {} v.s. required {})?
  3. Why allowing function type aliasing on tuple structs (e.g. the s_tuple(42) stuff)? That doesn't seem helping in any situations, and it creates confusions especially when your tuple struct is empty (the SEmptyTuple v.s. SEmptyTuple() declaration, two totally distinct types).

1. Empty fields with braces and parens are allowed since 1.15.0

Structs and tuple variants with zero fields and allowed by RFC 1506.
(This part of the RFC is stabilized since 1.15.0.)

struct SNah; is natural for the humanity, and additionally it also implicitly expresses "No fields would be added in future".

struct SEmpty {} is easy to generate automatically from another program or macros.
i.e. programs may write struct FieldsProvidedByCssFileAtCompileTime { /* fields here */ }, but the CSS file may empty (or specify no fields).

Additionally, the syntax does not lose consistency if it allows zero fields.
Rust allows some weird syntax which makes it easy to generate codes from programs.
Note that, they seems weird for humans, but the syntax definitions are very natural, simple, and consistent.

2. SNah is not only a struct name but also a constant

When a struct is defined without braces, it also defines a constant with that name.
In your example, you can use SNah as an expression, as if there is implicit const SNah: SNah = SNah {}; inserted.

OTOH, the definition struct SNah {} does not generate such implicit constant.

A unit struct expression is just the path to a unit struct item. This refers to the unit struct's implicit constant of its value. The unit struct value can also be constructed with a fieldless struct expression.

https://doc.rust-lang.org/reference/expressions/struct-expr.html#unit-struct-expression

3. Tuple struct names are also a function

Similar to 2., struct SEmptyTuple(); and struct STuple(i32); defines the functions (constructors) with the same names.
Actually, when you write let fortytwo = STuple(42);, you are calling the function STuple with an argument 42!

A struct expression with fields enclosed in parentheses constructs a tuple struct. Though it is listed here as a specific expression for completeness, it is equivalent to a call expression to the tuple struct's constructor.

https://doc.rust-lang.org/reference/expressions/struct-expr.html#tuple-struct-expression

4 Likes

Nuance of struct Snah; and struct SEmpty {}, and others

This is my personal thought, but I believe many people thinks like this.

  • You should use struct Snah; style, when you are sure you won't add any fields in future.
    • This is strong guarantee.
      When you add a field in future, many user-site codes would be broken.
  • When you don't want to guarantee that there are no additional fields in future, you should use zero-sized private field.
    • struct Foo(()); is widely used (even in std!)
    • If you want to use braces (to keep git diffs minimal in future), you can also use struct Foo { _dummy: () } or something like that.
  • Don't use struct SEmpty {} and struct SEmptyTuple(); styles definition for your hand-written code.
    • Always prefer two candidates listed above.
    • It does not provide convenient SEmpty constant.
      Users are forced to specify {} every time.
    • It does not guarantee that the struct won't have additional fields in future.
      Any field additions will break user codes.
4 Likes

Wow these are really extensive references! Deep thanks for spreading the knowledge!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.