Clarify the relationships between various kinds of structs and variants #1506

Open
wants to merge 2 commits into from

6 participants

@petrochenkov

Provide a simple model describing three kinds of structs and variants and their relationships + some small language extensions following from this model.

Rendered

cc rust-lang/rust#31757 (comment)

@arielb1 arielb1 and 1 other commented on an outdated diff Feb 22, 2016
text/0000-adt-kinds.md
+```
+struct US {}
+```
+
+and a constant with the same name
+
+```
+const US: US = US{};
+```
+
+Unit structs have 0 fields and are defined in both type (the type `US`) and value (the
+constant `US`) namespaces.
+
+As a basic struct, a unit struct can participate in struct expressions `US{}`, including FRU
+`US{..s}` and in struct patterns `US{}`/`US{..}`. In both cases the path `US` of the expression
+or pattern is looked up in the type namespace.
@arielb1
arielb1 added a note Feb 22, 2016

A unit struct/variant pattern is a struct/variant pattern, not a constant pattern, and therefore is exhaustive.

There's a "Note 1" below (I could expand it though).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@arielb1 arielb1 and 1 other commented on an outdated diff Feb 22, 2016
text/0000-adt-kinds.md
+ field2: Type2,
+ field3: Type3,
+}
+```
+
+Braced structs are the basic struct kind, other kinds are built on top of them.
+Braced structs have 0 or more user-named fields and are defined only in type namespace.
+
+Braced structs can be used in struct expressions `S{field1: expr, field2: expr}`, including
+functional record update (FRU) `S{field1: expr, ..s}`/`S{..s}` and with struct patterns
+`S{field1: pat, field2: pat}`/`S{field1: pat, ..}`/`S{..}`.
+In all cases the path `S` of the expression or pattern is looked up in the type namespace.
+Fields of a braced struct can be accessed with dot syntax `s.field1`.
+
+Note: struct *variants* are currently defined in the value namespace in addition to type namespace,
+ there are no particular reasons for this and this is probably temporary.
@arielb1
arielb1 added a note Feb 22, 2016

Do these have any use?

No. (Not now at least.)
See rust-lang/rust#30882 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@oli-obk oli-obk commented on the diff Feb 22, 2016
text/0000-adt-kinds.md
+As a constructor, a tuple struct can participate in tuple struct expressions `TS(expr, expr)` and
+tuple struct patterns `TS(pat, pat)`/`TS(..)`, both of these are looked up in the value namespace
+in which the constructor `TS` is defined. Tuple struct expressions `TS(expr, expr)` are usual
+function calls, but the compiler reserves the right to make observable improvements to them based
+on the additional knowledge, that `TS` is a constructor.
+
+Note: the automatically assigned field names are quite interesting, they are not identifiers
+lexically (they are integer literals), so such fields can't be defined manually.
+
+## Summary of the changes.
+
+Everything related to braced structs and unit structs is already implemented.
+
+New: Permit tuple structs and tuple variants with 0 fields. This restriction is artificial and can
+be lifted trivially. Macro writers dealing with tuple structs/variants will be happy to get rid of
+this one special case.
@oli-obk
oli-obk added a note Feb 22, 2016

examples of crates that had to implement workarounds because of this limitation?

I have an anecdotal evidence from @DanielKeep but no concrete examples so far.

@durka
durka added a note Feb 22, 2016

A more subtle question might be crates that are broken, but haven't noticed yet, because nobody tried to make it generate a struct with no fields.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ubsan

I really like these ideas. If they're not taken up by Rust, I may steal them for my own language :)

@petrochenkov petrochenkov Some improvements
Mention type aliases
Mention differences between unit structs / tuple struct constructors and `const` / `fn` items.
Add more use cases for `TS{0: expr}`/`TS{0: pat}`
433edd9
@alexcrichton alexcrichton added the T-lang label Mar 7, 2016
@nrc nrc commented on the diff Apr 27, 2016
text/0000-adt-kinds.md
+ field3: Type3,
+}
+```
+
+Braced structs are the basic struct kind, other kinds are built on top of them.
+Braced structs have 0 or more user-named fields and are defined only in type namespace.
+
+Braced structs can be used in struct expressions `S{field1: expr, field2: expr}`, including
+functional record update (FRU) `S{field1: expr, ..s}`/`S{..s}` and with struct patterns
+`S{field1: pat, field2: pat}`/`S{field1: pat, ..}`/`S{..}`.
+In all cases the path `S` of the expression or pattern is looked up in the type namespace (so these
+expressions/patterns can be used with type aliases).
+Fields of a braced struct can be accessed with dot syntax `s.field1`.
+
+Note: struct *variants* are currently defined in the value namespace in addition to type namespace,
+ there are no particular reasons for this and this is probably temporary.
@nrc
nrc added a note Apr 27, 2016

I guess this is because regular variants are in both namespaces and braced variants are a mistake. However, if we one day get named fields for functions, then I guess we could use struct names as ctor functions, so it might be useful. Too bad that ship has sailed for regular structs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@nrc nrc commented on the diff Apr 27, 2016
text/0000-adt-kinds.md
+Unit structs are defined without any fields or brackets.
+
+```
+struct US;
+```
+
+Unit structs can be thought of as a single declaration for two things: a basic struct
+
+```
+struct US {}
+```
+
+and a constant with the same name<sup>Note 1</sup>
+
+```
+const US: US = US{};
@nrc
nrc added a note Apr 27, 2016

c.f., an actual braced struct with zero fields, which is not in the value namespace

@durka
durka added a note Apr 27, 2016

I think that's the point. An empty struct-defined-with-without-braces is like an empty struct-defined-with-braces, plus a const like this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@nrc nrc commented on the diff Apr 27, 2016
text/0000-adt-kinds.md
+tuple struct patterns `TS(pat, pat)`/`TS(..)`, both of these are looked up in the value namespace
+in which the constructor `TS` is defined (so these expressions/patterns cannot be used with type
+aliases). Tuple struct expressions `TS(expr, expr)` are usual
+function calls, but the compiler reserves the right to make observable improvements to them based
+on the additional knowledge, that `TS` is a constructor.
+
+Note 1: the automatically assigned field names are quite interesting, they are not identifiers
+lexically (they are integer literals), so such fields can't be defined manually.
+Note 2: the constructor function is not exactly a `fn` item, there are subtle differences (e.g. with
+regards to privacy checks), but it's a close approximation.
+
+## Summary of the changes.
+
+Everything related to braced structs and unit structs is already implemented.
+
+New: Permit tuple structs and tuple variants with 0 fields. This restriction is artificial and can
@nrc
nrc added a note Apr 27, 2016

Could you expand on the exact semantics here, I assume there is no interoperability between the different kinds of zero-sized structs. Whereas, a unit struct can be thought of as a single constant object, I would think of zero-field tuple structs generating a potentially infinite number of unique instances, but I can't think of how this makes an actual difference in code.

@durka
durka added a note Apr 27, 2016

a unit struct can be thought of as a single constant object, I would think of zero-field tuple structs generating a potentially infinite number of unique instances

Can you expand on your intuition here?

struct TS(); is roughly equivalent to

struct TS {
}

 fn TS() -> TS {
     TS{}
 }

, it's described in the section about tuple structs above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@nrc nrc commented on the diff Apr 27, 2016
text/0000-adt-kinds.md
+
+## Summary of the changes.
+
+Everything related to braced structs and unit structs is already implemented.
+
+New: Permit tuple structs and tuple variants with 0 fields. This restriction is artificial and can
+be lifted trivially. Macro writers dealing with tuple structs/variants will be happy to get rid of
+this one special case.
+
+New: Permit using tuple structs and tuple variants in braced struct patterns and expressions not
+requiring naming their fields - `TS{..ts}`/`TS{}`/`TS{..}`. This doesn't require much effort to
+implement as well.
+This also means that `S{..}` patterns can be used to match structures and variants of any kind.
+The desire to have such "match everything" patterns is sometimes expressed given
+that number of fields in structures and variants can change from zero to non-zero and back during
+development.
@nrc
nrc added a note Apr 27, 2016

This is definitely desirable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@nrc nrc commented on the diff Apr 27, 2016
text/0000-adt-kinds.md
+that number of fields in structures and variants can change from zero to non-zero and back during
+development.
+An extra benefit is ability to match/construct tuple structs using their type aliases.
+
+New: Permit using tuple structs and tuple variants in braced struct patterns and expressions
+requiring naming their fields - `TS{0: expr}`/`TS{0: pat}`/etc.
+While this change is important for consistency, there's not much motivation for it in hand-written
+code besides shortening patterns like `ItemFn(_, _, unsafety, _, _, _)` into something like
+`ItemFn{2: unsafety, ..}` and ability to match/construct tuple structs using their type aliases.
+However, automatic code generators (e.g. syntax extensions) can get more benefits from the
+ability to generate uniform code for all structure kinds.
+`#[derive]` for example, currently has separate code paths for generating expressions and patterns
+for braces structs (`ExprStruct`/`PatKind::Struct`), tuple structs
+(`ExprCall`/`PatKind::TupleStruct`) and unit structs (`ExprPath`/`PatKind::Path`). With proposed
+changes `#[derive]` could simplify its logic and always generate braced forms for expressions and
+patterns.
@nrc
nrc added a note Apr 27, 2016

This makes me uncomfortable since it weakens the correspondence between the declaration, pattern, and creation syntaxes. And makes the bracing more fuzzy, where we've been trying to be stricter. Since the only motivation is in procedural macros, I wonder if there is a solution using clever libraries or tooling, rather than changing the language?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@aturon aturon added the I-nominated label Jul 28, 2016
@petrochenkov
petrochenkov commented Jul 31, 2016 edited

Note that everything the implementation does (modulo tests and feature gates) is removing non-fatal errors and accepting a new grammar production in the parser, i.e. the RFC only legitimizes things that already work internally.

@Mark-Simulacrum Mark-Simulacrum commented on the diff Jul 31, 2016
text/0000-adt-kinds.md
+
+As a basic struct, a tuple struct can participate in struct expressions `TS{0: expr, 1: expr}`,
+including FRU `TS{0: expr, ..ts}`/`TS{..ts}` and in struct patterns
+`TS{0: pat, 1: pat}`/`TS{0: pat, ..}`/`TS{..}`.
+In both cases the path `TS` of the expression or pattern is looked up in the type namespace (so
+these expressions/patterns can be used with type aliases).
+Fields of a tuple struct can be accessed with dot syntax `ts.0`.
+
+As a constructor, a tuple struct can participate in tuple struct expressions `TS(expr, expr)` and
+tuple struct patterns `TS(pat, pat)`/`TS(..)`, both of these are looked up in the value namespace
+in which the constructor `TS` is defined (so these expressions/patterns cannot be used with type
+aliases). Tuple struct expressions `TS(expr, expr)` are usual
+function calls, but the compiler reserves the right to make observable improvements to them based
+on the additional knowledge, that `TS` is a constructor.
+
+Note 1: the automatically assigned field names are quite interesting, they are not identifiers

This note doesn't appear to be used in the above section.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@nikomatsakis

Hear ye, hear ye! This RFC is now entering final comment period. In the most recent meeting, @nrc and I (the only two in attendance) both felt inclined to accept (though no final decision has been reached).

In general, the feeling is that this RFC is not needed in any strict sense, though enabling Foo { .. } as a generic "variant check" pattern is pretty useful. It does seem to generally move {} vs () into a more strict relationship, which seems useful, though potentially more confusing (perhaps a stricter separation is clearer?).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment