From 3d00adde7c31736e65abd0e9a6cb5829ac005732 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 28 Aug 2014 17:36:55 +0200 Subject: [PATCH 01/14] initial draft still a few things to flesh out i thin. --- active/0000-empty-structs-with-braces.md | 158 +++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 active/0000-empty-structs-with-braces.md diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md new file mode 100644 index 00000000000..12be0177d17 --- /dev/null +++ b/active/0000-empty-structs-with-braces.md @@ -0,0 +1,158 @@ +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +When a struct type `S` has no fields (a so-called "empty struct"): + + * allow `S` to be defined via either `struct S;` (as today) + or `struct S {}` (new) + * allow instances of `S` to be constructed via either the + expression `S` (as today) or the expression `S {}` (new) + * allow instances of `S` to be pattern matched via either the + pattern `S` (as today) or the pattern `S {}` (new). + +# Motivation + +Today, when writing code, one must treat an empty struct as a +special case, distinct from structs that include fields. +That is, one must write code like this: +```rust +struct S2 { x1: int, x2: int } +struct S0; // kind of different from the above. + +let s2 = S2 { x1: 1, x2: 2 }; +let s0 = S0; // kind of different from the above. + +match (s2, s0) { + (S2 { x1: y1, x2: y2 }, + S0) // you can see my pattern here + => { println!("Hello from S2({}, {}) and S0", y1, y2); } +} +``` + +While this yields code that is relatively free of extraneous +curly-braces, this special case handling of empty structs presents +problems for two cases of interest: code generators (including, but +not limited to, Rust macros) and conditionalized code (i.e. code with +`cfg` attributes). + +The special case handling of empty structs is also a problem for +programmers who actively add and remove fields from structs during +development; such changes cause a struct to switch from being empty +and non-empty, and the associated revisions of changing removing and +adding curly braces is aggravating (both in effort revising the code, +and also in extra noise introduced into commit histories). + +This RFC proposes going back to the state we were in circa February +2013, when both `S0` and `S0 { }` were accepted syntaxes for an empty +struct. The parsing ambiguity that motivated removing support for +`S0 { }` is no longer present (see [#ancient_history]). + + +# Detailed design + +Revise the grammar of struct item definitions so that one can write +either `struct S;` or `struct S { }`. The two forms are synonymous. +The first is preferred with respect to coding style; for example, the +first is emitted by the pretty printer. + +Revise the grammar of expressions and patterns so that, when `S` is an +empty struct, one can write either `S` or `S { }`. The two forms are +synonymous. Again, the first is preferred with respect to coding style, +and is emitted by the pretty printer. + +# Drawbacks + +Some people like "There is only one way to do it." But, there is +precendent in Rust for violating "one way to do it" in favor of +syntactic convenience or regularity; see +[#precedent_for_flexible_syntax_in_rust]. +Also, see Alternative 1 below. + +# Alternatives + +Alternative 1: Require empty curly braces on empty structs. + +Alternative 2: Status quo. Macros and code-generators in general +will need to handle empty structs as a special case. We may +continue hitting bugs like + +# Unresolved questions + +None. + +# Appendices + +## Ancient History + +A parsing ambiguity was the original motivation for disallowing the +syntax `struct S {}` in favor of `struct S;` for an empty struct +declaration. The ambiguity and various options for dealing with it +were well documented on the [associated mailing list thread][RustDev +Thread]. Both syntaxes were simultaneously supported at the time. +Support for `struct S {}` was removed because that was the most +expedient option. In particular, at that time, the option of "Place a +parser restriction on those contexts where `{` terminates the +expression and say that struct literals cannot appear there unless +they are in parentheses." was explicitly not chosen, in favor of +continuing to use the disambiguation rule in use at the time, namely +that the presence of a label (e.g. `S { a_label: ... }`) was *the* way +to distinguish a struct constructor from an identifier followed by a +control block, and thus, "there must be one label." + +In particular, at the time that mailing list thread was created, the +code match `match x {} ...` would be parsed as `match (x {}) ...`, not +as `(match x {}) ...` (see [Rust PR 5137]); likewise, `if x {}` would +be parsed as an if-expression whose test component is the struct +literal `x {}`. Thus, at the time of [Rust PR 5137], if the input to +a `match` or `if` was an identifier expression, one had to put +parentheses around the identifier to force it to be interpreted as +input, and not as a struct constructor. + +Things have changed since then; namely, we have now adopted the +aforementioned parser restriction [Rust RFC 25]. (The text of RFC 25 +does not explicitly address `match`, but we have effectively expanded +it to include a curly-brace delimited block of match-arms in the +definition of "block".) Today, one uses parentheses around struct +literals in some contexts (such as `for e in (S {x: 3}) { ... }` or +`match (S {x: 3}) { ... }` + +## Precedent for flexible syntax in Rust + +There is precendent in Rust for violating "one way to do it" in favor +of syntactic convenience or regularity. + +For example, one can often include an optional trailing comma, for +example in: `let x : &[int] = [3, 2, 1, ];`. + +One can also include redundant curly braces or parentheses, for +example in: +```rust +println!("hi: {}", { if { x.len() > 2 } { ("whoa") } else { ("there") } }); +``` + +One can even mix the two together when delimiting match arms: +```rust + let z: int = match x { + [3, 2] => { 3 } + [3, 2, 1] => 2, + _ => { 1 }, + }; +``` + +We do have lints for some style violations (though none catch the +cases above), but lints are different from fundamental language +restrictions. + + +[RustDev Thread]: https://mail.mozilla.org/pipermail/rust-dev/2013-February/003282.html + +[Rust Issue 5167]: https://github.com/rust-lang/rust/issues/5167 + +[Rust RFC 25]: https://github.com/rust-lang/rfcs/blob/master/complete/0025-struct-grammar.md + +[CFG parse bug]: https://github.com/rust-lang/rust/issues/16819 + +[Rust PR 5137]: https://github.com/rust-lang/rust/pull/5137 From 99301be11e48d0ee64676e90f8a83576dfd189fa Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 11:43:26 +0200 Subject: [PATCH 02/14] fleshed out remainder of rfc. --- active/0000-empty-structs-with-braces.md | 186 ++++++++++++++++++++--- 1 file changed, 167 insertions(+), 19 deletions(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 12be0177d17..2aefdc11bf8 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -1,17 +1,13 @@ -- Start Date: (fill me in with today's date, YYYY-MM-DD) +- Start Date: (fill me in with today's date, 2014-08-28) - RFC PR: (leave this empty) - Rust Issue: (leave this empty) # Summary -When a struct type `S` has no fields (a so-called "empty struct"): - - * allow `S` to be defined via either `struct S;` (as today) - or `struct S {}` (new) - * allow instances of `S` to be constructed via either the - expression `S` (as today) or the expression `S {}` (new) - * allow instances of `S` to be pattern matched via either the - pattern `S` (as today) or the pattern `S {}` (new). +When a struct type `S` has no fields (a so-called "empty struct"), +allow it to be defined via either `struct S;` or `struct S {}`, and +allow instances of it to be constructed and pattern-matched via either +`S` or `S {}`. # Motivation @@ -34,9 +30,14 @@ match (s2, s0) { While this yields code that is relatively free of extraneous curly-braces, this special case handling of empty structs presents -problems for two cases of interest: code generators (including, but -not limited to, Rust macros) and conditionalized code (i.e. code with -`cfg` attributes). +problems for two cases of interest: automatic code generators +(including, but not limited to, Rust macros) and conditionalized code +(i.e. code with `cfg` attributes; see appendix [#the_cfg_problem]). +The heart of the code-generator argument is: Why force all +to-be-written code-generators and macros with special-case handling of +the empty struct case (in terms of whether or not to include the +surrounding braces), especially since that special case is likely to +be forgotten (yielding a latent bug in the code generator). The special case handling of empty structs is also a problem for programmers who actively add and remove fields from structs during @@ -49,10 +50,20 @@ This RFC proposes going back to the state we were in circa February 2013, when both `S0` and `S0 { }` were accepted syntaxes for an empty struct. The parsing ambiguity that motivated removing support for `S0 { }` is no longer present (see [#ancient_history]). - +Supporting empty braces in the syntax for empty structs is easy to do +in the language now. # Detailed design + * Allow `S` to be defined via either `struct S;` (as today) + or `struct S {}` (new) + + * Allow instances of `S` to be constructed via either the + expression `S` (as today) or the expression `S {}` (new) + + * Allow instances of `S` to be pattern matched via either the + pattern `S` (as today) or the pattern `S {}` (new). + Revise the grammar of struct item definitions so that one can write either `struct S;` or `struct S { }`. The two forms are synonymous. The first is preferred with respect to coding style; for example, the @@ -60,20 +71,39 @@ first is emitted by the pretty printer. Revise the grammar of expressions and patterns so that, when `S` is an empty struct, one can write either `S` or `S { }`. The two forms are -synonymous. Again, the first is preferred with respect to coding style, -and is emitted by the pretty printer. +synonymous. Again, the first is preferred with respect to coding +style, and is emitted by the pretty printer. + +The format of the definiton has no bearing on the format of the +expressions or pattern forms; either syntax can be used for any +empty-struct, regardless of how it is defined. + +There is no ambiguity introduced by this change, because we have +already introduced a restriction to the Rust grammar to force the use +of parentheses to disambiguate struct literals in such contexts. (See +[Rust RFC 25]). + # Drawbacks Some people like "There is only one way to do it." But, there is precendent in Rust for violating "one way to do it" in favor of syntactic convenience or regularity; see -[#precedent_for_flexible_syntax_in_rust]. -Also, see Alternative 1 below. +the appendix +[Precedent for flexible syntax in Rust][#precedent_for_flexible_syntax_in_rust]. +Also, see Alternative 1: "Always Require Braces" below. # Alternatives -Alternative 1: Require empty curly braces on empty structs. +Alternative 1: "Always Require Braces". Specifically, require empty +curly braces on empty structs. People who like the current syntax of +curly-brace free structs can encode them this way: `enum S0 { S0 }` +This would address all of the same issues outlined above. (Also, the +author (pnkfelix) would be happy to take this tack.) The main reason +not to take this tack is that some people may like writing empty +structs without braces, but do not want to switch to the unary enum +version. See "I wouldn't want to force noisier syntax ..." in +[#recent_history]. Alternative 2: Status quo. Macros and code-generators in general will need to handle empty structs as a special case. We may @@ -81,10 +111,102 @@ continue hitting bugs like # Unresolved questions -None. +## Empty Tuple Structs + +The code-generation argument could be applied to tuple-structs as +well, to claim that we should allow the syntax `S0()`. I am less +inclined to add a special case for that. Note that we should not +attempt to generalize this RFC as proposed to include tuple structs, +i.e. so that given `struct S0 {}`, the expressions `T0`, `T0 {}`, and +`T0()` would be synonymous. The reason is that +given a tuple struct `struct T2(int, int)`, the identifier `T2` is +*already* bound to the constructor function: + +```rust +fn main() { + #[deriving(Show)] + struct T2(int, int); + + fn foo(f: |int, int| -> S) { + println!("Hello from {} and {}", f(2,3), f(4,5)); + } + foo(T2); +} +``` + +So if we were to attempt to generalize the leniency of this RFC to +tuple structs, we would be in the unfortunate situation given `struct +T0();` of trying to treat `T0` simultaneously as an instance of the +struct and as a constructor function. So, the handling of empty +structs proposed by this RFC does not generalize to tuple structs. + +(Note that if we adopt alternative 1, then the issue of how tuple +structs are handled is totally orthogonal -- we could add support for +`struct T0()` as a distinct type from `struct S0 {}`, if we so wished, +or leave it aside.) # Appendices +## The CFG problem + +A program like this works today: + +```rust +fn main() { + #[deriving(Show)] + struct Svaries { + x: int, + y: int, + + #[cfg(zed)] + z: int, + } + + let s = match () { + #[cfg(zed)] _ => Svaries { x: 3, y: 4, z: 5 }, + #[cfg(not(zed))] _ => Svaries { x: 3, y: 4 }, + }; + println!("Hello from {}", s) +} +``` + +Observe what happens when one modifies the above just a bit: +```rust + struct Svaries { + #[cfg(eks)] + x: int, + #[cfg(why)] + y: int, + + #[cfg(zed)] + z: int, + } +``` + +Now, certain `cfg` settings yield an empty struct, even though it +is surrounded by braces. Today this leads to a [CFG parse bug]. + +If we want to support situations like this properly, we will probably +need to further extend the `cfg` attribute so that it can be placed +before individual fields in a struct constructor, like this: + +```rust +// You cannot do this today, +// but maybe in the future (after a different RFC) +let s = Svaries { + #[cfg(eks)] x: 3, + #[cfg(why)] y: 4, + #[cfg(zed)] z: 5, +}; +``` + +Supporting such a syntax consistently in the future should start today +with allowing empty braces as legal code. (Strictly speaking, it is +not *necessary* that we add support for empty braces at the parsing +level to support this feature at the semantic level. But supporting +empty-braces in the syntax still seems like the most consistent path +to me.) + ## Ancient History A parsing ambiguity was the original motivation for disallowing the @@ -119,6 +241,10 @@ definition of "block".) Today, one uses parentheses around struct literals in some contexts (such as `for e in (S {x: 3}) { ... }` or `match (S {x: 3}) { ... }` +Note that there was never an ambiguity for uses of `struct S0 { }` in item +position. The issue was solely about expression position prior to the +adoption of [Rust RFC 25]. + ## Precedent for flexible syntax in Rust There is precendent in Rust for violating "one way to do it" in favor @@ -146,6 +272,26 @@ We do have lints for some style violations (though none catch the cases above), but lints are different from fundamental language restrictions. +## Recent history + +There was a previous [RFC PR][RFC PR 147] that was effectively the +same in spirit to this one. It was closed because it was not +sufficient well fleshed out for further consideration by the core +team. However, to save people the effort of reviewing the comments on +that PR (and hopefully stave off potential bikeshedding on this PR), I +here summarize the various viewpoints put forward on the comment +thread there, and note for each one, whether that viewpoint would be +addressed by this RFC (accept both syntaxes), by Alternative 1 (accept +only `S0 {}`), or by the status quo (accept only `S0`). + + + +* "I find `let s = S0;` jarring, think its an enum initially." ==> Favors: Alternative 1 +* "Frequently start out with an empty struct and add fields as I need them." ==> Favors: This RFC or Alternative 1 +* "Foo{} suggests is constructing something that it's not; all uses of the value `Foo` are indistinguishable from each other" ==> Favors: Status Quo +* "I find it strange anyone would prefer `let x = Foo{};` over `let x = Foo;`" ==> Favors Status Quo; strongly opposes Alternative 1. +* "I agree that 'instantiation-should-follow-declation', that is, structs declared `;, (), {}` should only be instantiated [via] `;, (), { }` respectively" ==> Opposes leniency of this RFC in that it allows expression to use include or omit `{}` on an empty struct, regardless of declaration form, and vice-versa. +* "The code generation argument is reasonable, but I wouldn't want to force noisier syntax on all 'normal' code just to make macros work better." ==> Favors: This RFC [RustDev Thread]: https://mail.mozilla.org/pipermail/rust-dev/2013-February/003282.html @@ -156,3 +302,5 @@ restrictions. [CFG parse bug]: https://github.com/rust-lang/rust/issues/16819 [Rust PR 5137]: https://github.com/rust-lang/rust/pull/5137 + +[RFC PR 147]: https://github.com/rust-lang/rfcs/pull/147 From 30873b058b4c71bba43949f5c72a4e0cbf5f6dd7 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 11:44:48 +0200 Subject: [PATCH 03/14] fixed a href link. --- active/0000-empty-structs-with-braces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 2aefdc11bf8..8188f0d13d1 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -32,7 +32,7 @@ While this yields code that is relatively free of extraneous curly-braces, this special case handling of empty structs presents problems for two cases of interest: automatic code generators (including, but not limited to, Rust macros) and conditionalized code -(i.e. code with `cfg` attributes; see appendix [#the_cfg_problem]). +(i.e. code with `cfg` attributes; see appendix [The CFG problem][#the_cfg_problem]). The heart of the code-generator argument is: Why force all to-be-written code-generators and macros with special-case handling of the empty struct case (in terms of whether or not to include the From 40e608cafa1e04b5a58228abf17bdc2a88043299 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 11:45:43 +0200 Subject: [PATCH 04/14] again attempt to fix href format. --- active/0000-empty-structs-with-braces.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 8188f0d13d1..854b4d3949a 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -32,7 +32,7 @@ While this yields code that is relatively free of extraneous curly-braces, this special case handling of empty structs presents problems for two cases of interest: automatic code generators (including, but not limited to, Rust macros) and conditionalized code -(i.e. code with `cfg` attributes; see appendix [The CFG problem][#the_cfg_problem]). +(i.e. code with `cfg` attributes; see appendix [The CFG problem]. The heart of the code-generator argument is: Why force all to-be-written code-generators and macros with special-case handling of the empty struct case (in terms of whether or not to include the @@ -293,6 +293,8 @@ only `S0 {}`), or by the status quo (accept only `S0`). * "I agree that 'instantiation-should-follow-declation', that is, structs declared `;, (), {}` should only be instantiated [via] `;, (), { }` respectively" ==> Opposes leniency of this RFC in that it allows expression to use include or omit `{}` on an empty struct, regardless of declaration form, and vice-versa. * "The code generation argument is reasonable, but I wouldn't want to force noisier syntax on all 'normal' code just to make macros work better." ==> Favors: This RFC +[The CFG problem]: #the_cfg_problem + [RustDev Thread]: https://mail.mozilla.org/pipermail/rust-dev/2013-February/003282.html [Rust Issue 5167]: https://github.com/rust-lang/rust/issues/5167 From 35a247ff49528ec2072b66939075880753f7db25 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 11:55:16 +0200 Subject: [PATCH 05/14] more cleanup, attempted to fix many urls. --- active/0000-empty-structs-with-braces.md | 74 ++++++++++++++---------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 854b4d3949a..33fd8947523 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -32,7 +32,7 @@ While this yields code that is relatively free of extraneous curly-braces, this special case handling of empty structs presents problems for two cases of interest: automatic code generators (including, but not limited to, Rust macros) and conditionalized code -(i.e. code with `cfg` attributes; see appendix [The CFG problem]. +(i.e. code with `cfg` attributes; see the [CFG problem] appendix). The heart of the code-generator argument is: Why force all to-be-written code-generators and macros with special-case handling of the empty struct case (in terms of whether or not to include the @@ -49,7 +49,7 @@ and also in extra noise introduced into commit histories). This RFC proposes going back to the state we were in circa February 2013, when both `S0` and `S0 { }` were accepted syntaxes for an empty struct. The parsing ambiguity that motivated removing support for -`S0 { }` is no longer present (see [#ancient_history]). +`S0 { }` is no longer present (see the [Ancient History] appendix). Supporting empty braces in the syntax for empty structs is easy to do in the language now. @@ -89,12 +89,13 @@ of parentheses to disambiguate struct literals in such contexts. (See Some people like "There is only one way to do it." But, there is precendent in Rust for violating "one way to do it" in favor of syntactic convenience or regularity; see -the appendix -[Precedent for flexible syntax in Rust][#precedent_for_flexible_syntax_in_rust]. -Also, see Alternative 1: "Always Require Braces" below. +the [Precedent for flexible syntax in Rust] appendix. +Also, see [Always Require Braces] alternative below. # Alternatives +## Always Require Braces + Alternative 1: "Always Require Braces". Specifically, require empty curly braces on empty structs. People who like the current syntax of curly-brace free structs can encode them this way: `enum S0 { S0 }` @@ -102,25 +103,32 @@ This would address all of the same issues outlined above. (Also, the author (pnkfelix) would be happy to take this tack.) The main reason not to take this tack is that some people may like writing empty structs without braces, but do not want to switch to the unary enum -version. See "I wouldn't want to force noisier syntax ..." in -[#recent_history]. +version. See "I wouldn't want to force noisier syntax ..." in the +[Recent History] appendix. -Alternative 2: Status quo. Macros and code-generators in general -will need to handle empty structs as a special case. We may -continue hitting bugs like +## Status quo -# Unresolved questions +Alternative 2: Status quo. Macros and code-generators in general will +need to handle empty structs as a special case. We may continue +hitting bugs like [CFG parse bug]. Some users will be annoyed but +most will probably cope. ## Empty Tuple Structs +One might say "why are you including support for curly braces, but not +parentheses?" Or in other words, "what about empty tuple structs?" + The code-generation argument could be applied to tuple-structs as well, to claim that we should allow the syntax `S0()`. I am less -inclined to add a special case for that. Note that we should not -attempt to generalize this RFC as proposed to include tuple structs, -i.e. so that given `struct S0 {}`, the expressions `T0`, `T0 {}`, and -`T0()` would be synonymous. The reason is that -given a tuple struct `struct T2(int, int)`, the identifier `T2` is -*already* bound to the constructor function: +inclined to add a special case for that; I think tuple-structs are +less frequently used (especially with many fields); they are largely +for ad-hoc data such as newtype wrappers, not for code generators. + +Note that we should not attempt to generalize this RFC as proposed to +include tuple structs, i.e. so that given `struct S0 {}`, the +expressions `T0`, `T0 {}`, and `T0()` would be synonymous. The reason +is that given a tuple struct `struct T2(int, int)`, the identifier +`T2` is *already* bound to a constructor function: ```rust fn main() { @@ -140,10 +148,14 @@ T0();` of trying to treat `T0` simultaneously as an instance of the struct and as a constructor function. So, the handling of empty structs proposed by this RFC does not generalize to tuple structs. -(Note that if we adopt alternative 1, then the issue of how tuple -structs are handled is totally orthogonal -- we could add support for -`struct T0()` as a distinct type from `struct S0 {}`, if we so wished, -or leave it aside.) +(Note that if we adopt alternative 1, [Always Require Braces], then +the issue of how tuple structs are handled is totally orthogonal -- we +could add support for `struct T0()` as a distinct type from `struct S0 +{}`, if we so wished, or leave it aside.) + +# Unresolved questions + +None # Appendices @@ -281,19 +293,23 @@ team. However, to save people the effort of reviewing the comments on that PR (and hopefully stave off potential bikeshedding on this PR), I here summarize the various viewpoints put forward on the comment thread there, and note for each one, whether that viewpoint would be -addressed by this RFC (accept both syntaxes), by Alternative 1 (accept -only `S0 {}`), or by the status quo (accept only `S0`). - - +addressed by this RFC (accept both syntaxes), by [Always Require Braces], +or by [Status Quo]. -* "I find `let s = S0;` jarring, think its an enum initially." ==> Favors: Alternative 1 -* "Frequently start out with an empty struct and add fields as I need them." ==> Favors: This RFC or Alternative 1 +* "I find `let s = S0;` jarring, think its an enum initially." ==> Favors: Always Require Braces +* "Frequently start out with an empty struct and add fields as I need them." ==> Favors: This RFC or Always Require Braces * "Foo{} suggests is constructing something that it's not; all uses of the value `Foo` are indistinguishable from each other" ==> Favors: Status Quo -* "I find it strange anyone would prefer `let x = Foo{};` over `let x = Foo;`" ==> Favors Status Quo; strongly opposes Alternative 1. +* "I find it strange anyone would prefer `let x = Foo{};` over `let x = Foo;`" ==> Favors Status Quo; strongly opposes Always Require Braces. * "I agree that 'instantiation-should-follow-declation', that is, structs declared `;, (), {}` should only be instantiated [via] `;, (), { }` respectively" ==> Opposes leniency of this RFC in that it allows expression to use include or omit `{}` on an empty struct, regardless of declaration form, and vice-versa. * "The code generation argument is reasonable, but I wouldn't want to force noisier syntax on all 'normal' code just to make macros work better." ==> Favors: This RFC -[The CFG problem]: #the_cfg_problem +[Always Require Braces]: #always-require-braces +[Status Quo]: #status-quo +[Ancient History]: #ancient-history +[Recent History]: #recent-history +[CFG problem]: #the-cfg-problem +[Empty Tuple Structs]: #empty-tuple-structs +[Precedent for flexible syntax in Rust]: #precedent-for-flexible-syntax-in-rust [RustDev Thread]: https://mail.mozilla.org/pipermail/rust-dev/2013-February/003282.html From ff628b341ba9bb81e44f77c39a5af186e29f0f21 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 11:58:00 +0200 Subject: [PATCH 06/14] clarify where the CFG parse bug actually arises so people are not scratching their heads. --- active/0000-empty-structs-with-braces.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 33fd8947523..77e471c03b2 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -196,7 +196,8 @@ Observe what happens when one modifies the above just a bit: ``` Now, certain `cfg` settings yield an empty struct, even though it -is surrounded by braces. Today this leads to a [CFG parse bug]. +is surrounded by braces. Today this leads to a [CFG parse bug] +when one attempts to actually construct such a struct. If we want to support situations like this properly, we will probably need to further extend the `cfg` attribute so that it can be placed From f7294bc88c321dfbbe1a496e0d812c6ca527ca10 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 12:04:41 +0200 Subject: [PATCH 07/14] Corrected some misstatements in the ancient history. --- active/0000-empty-structs-with-braces.md | 47 ++++++++++++++---------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 77e471c03b2..4ca67b2cba2 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -223,19 +223,10 @@ to me.) ## Ancient History A parsing ambiguity was the original motivation for disallowing the -syntax `struct S {}` in favor of `struct S;` for an empty struct -declaration. The ambiguity and various options for dealing with it +syntax `S {}` in favor of `S` for constructing an instance of +an empty struct. The ambiguity and various options for dealing with it were well documented on the [associated mailing list thread][RustDev Thread]. Both syntaxes were simultaneously supported at the time. -Support for `struct S {}` was removed because that was the most -expedient option. In particular, at that time, the option of "Place a -parser restriction on those contexts where `{` terminates the -expression and say that struct literals cannot appear there unless -they are in parentheses." was explicitly not chosen, in favor of -continuing to use the disambiguation rule in use at the time, namely -that the presence of a label (e.g. `S { a_label: ... }`) was *the* way -to distinguish a struct constructor from an identifier followed by a -control block, and thus, "there must be one label." In particular, at the time that mailing list thread was created, the code match `match x {} ...` would be parsed as `match (x {}) ...`, not @@ -244,15 +235,31 @@ be parsed as an if-expression whose test component is the struct literal `x {}`. Thus, at the time of [Rust PR 5137], if the input to a `match` or `if` was an identifier expression, one had to put parentheses around the identifier to force it to be interpreted as -input, and not as a struct constructor. - -Things have changed since then; namely, we have now adopted the -aforementioned parser restriction [Rust RFC 25]. (The text of RFC 25 -does not explicitly address `match`, but we have effectively expanded -it to include a curly-brace delimited block of match-arms in the -definition of "block".) Today, one uses parentheses around struct -literals in some contexts (such as `for e in (S {x: 3}) { ... }` or -`match (S {x: 3}) { ... }` +input to the `match`/`if`, and not as a struct constructor. + +Of the options for resolving this discussed on the mailing list +thread, the one selected (removing `S {}` construction expressions) +was chosen as the most expedient option. + +At that time, the option of "Place a parser restriction on those +contexts where `{` terminates the expression and say that struct +literals cannot appear there unless they are in parentheses." was +explicitly not chosen, in favor of continuing to use the +disambiguation rule in use at the time, namely that the presence of a +label (e.g. `S { a_label: ... }`) was *the* way to distinguish a +struct constructor from an identifier followed by a control block, and +thus, "there must be one label." + +Naturally, if the construction syntax were to be disallowed, it made +sense to also remove the `struct S {}` declaration syntax. + +Things have changed since the time of that mailing list thread; +namely, we have now adopted the aforementioned parser restriction +[Rust RFC 25]. (The text of RFC 25 does not explicitly address +`match`, but we have effectively expanded it to include a curly-brace +delimited block of match-arms in the definition of "block".) Today, +one uses parentheses around struct literals in some contexts (such as +`for e in (S {x: 3}) { ... }` or `match (S {x: 3}) { ... }` Note that there was never an ambiguity for uses of `struct S0 { }` in item position. The issue was solely about expression position prior to the From 2470b6dd1608346395eeed08fe562a50f82039c3 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 12:06:10 +0200 Subject: [PATCH 08/14] fixed presentation of mailing list thread hyperlink. --- active/0000-empty-structs-with-braces.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 4ca67b2cba2..114da96636b 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -225,8 +225,8 @@ to me.) A parsing ambiguity was the original motivation for disallowing the syntax `S {}` in favor of `S` for constructing an instance of an empty struct. The ambiguity and various options for dealing with it -were well documented on the [associated mailing list thread][RustDev -Thread]. Both syntaxes were simultaneously supported at the time. +were well documented on the [rust-dev thread]. +Both syntaxes were simultaneously supported at the time. In particular, at the time that mailing list thread was created, the code match `match x {} ...` would be parsed as `match (x {}) ...`, not @@ -319,7 +319,7 @@ or by [Status Quo]. [Empty Tuple Structs]: #empty-tuple-structs [Precedent for flexible syntax in Rust]: #precedent-for-flexible-syntax-in-rust -[RustDev Thread]: https://mail.mozilla.org/pipermail/rust-dev/2013-February/003282.html +[rust-dev thread]: https://mail.mozilla.org/pipermail/rust-dev/2013-February/003282.html [Rust Issue 5167]: https://github.com/rust-lang/rust/issues/5167 From d6a06629153ea579c0e80e6fee78efac237fd23f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 12:13:51 +0200 Subject: [PATCH 09/14] Okay I think this is about ready to post now. --- active/0000-empty-structs-with-braces.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 114da96636b..d9e2496fbc2 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -90,7 +90,12 @@ Some people like "There is only one way to do it." But, there is precendent in Rust for violating "one way to do it" in favor of syntactic convenience or regularity; see the [Precedent for flexible syntax in Rust] appendix. -Also, see [Always Require Braces] alternative below. +Also, see the [Always Require Braces] alternative below. + +I have attempted to summarize the previous discussion from [RFC PR +147] in the [Recent History] appendix; some of the points there +include drawbacks to this approach and to the [Always Require Braces] +alternative. # Alternatives @@ -100,11 +105,12 @@ Alternative 1: "Always Require Braces". Specifically, require empty curly braces on empty structs. People who like the current syntax of curly-brace free structs can encode them this way: `enum S0 { S0 }` This would address all of the same issues outlined above. (Also, the -author (pnkfelix) would be happy to take this tack.) The main reason -not to take this tack is that some people may like writing empty -structs without braces, but do not want to switch to the unary enum -version. See "I wouldn't want to force noisier syntax ..." in the -[Recent History] appendix. +author (pnkfelix) would be happy to take this tack.) + +The main reason not to take this tack is that some people may like +writing empty structs without braces, but do not want to switch to the +unary enum version. See "I wouldn't want to force noisier syntax ..." +in the [Recent History] appendix. ## Status quo From e5eecb0d7e5c78dcd9cc6f3a6b232376edcfc25c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 12:16:02 +0200 Subject: [PATCH 10/14] Could not resist adding air-quotes to "new". --- active/0000-empty-structs-with-braces.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index d9e2496fbc2..9f5cf7d3c90 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -56,13 +56,13 @@ in the language now. # Detailed design * Allow `S` to be defined via either `struct S;` (as today) - or `struct S {}` (new) + or `struct S {}` ("new") * Allow instances of `S` to be constructed via either the - expression `S` (as today) or the expression `S {}` (new) + expression `S` (as today) or the expression `S {}` ("new") * Allow instances of `S` to be pattern matched via either the - pattern `S` (as today) or the pattern `S {}` (new). + pattern `S` (as today) or the pattern `S {}` ("new"). Revise the grammar of struct item definitions so that one can write either `struct S;` or `struct S { }`. The two forms are synonymous. From a62d42abe031fa00399dfd351520de4bce79aab3 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 12:23:32 +0200 Subject: [PATCH 11/14] Add a disclaimer about the Recent History summary. --- active/0000-empty-structs-with-braces.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 9f5cf7d3c90..4606e937eb1 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -310,6 +310,11 @@ thread there, and note for each one, whether that viewpoint would be addressed by this RFC (accept both syntaxes), by [Always Require Braces], or by [Status Quo]. +Note that this list of comments is *just* meant to summarize the list +of views; it does not attempt to reflect the number of commenters who +agreed or disagreed with a particular point. (But since the RFC process +is not a democracy, the number of commenters should not matter anyway.) + * "I find `let s = S0;` jarring, think its an enum initially." ==> Favors: Always Require Braces * "Frequently start out with an empty struct and add fields as I need them." ==> Favors: This RFC or Always Require Braces * "Foo{} suggests is constructing something that it's not; all uses of the value `Foo` are indistinguishable from each other" ==> Favors: Status Quo From 936bb323484b8c2a4ea61cf0dd890bbcf1c61d81 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 29 Aug 2014 12:26:36 +0200 Subject: [PATCH 12/14] Add a "+1" to the Recent History summary. Figure I should at least let those commenters get a bucket in the summary, even though I personally am ambivalent at best about "+1" comments. --- active/0000-empty-structs-with-braces.md | 1 + 1 file changed, 1 insertion(+) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 4606e937eb1..512b410e6a5 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -315,6 +315,7 @@ of views; it does not attempt to reflect the number of commenters who agreed or disagreed with a particular point. (But since the RFC process is not a democracy, the number of commenters should not matter anyway.) +* "+1" ==> Favors: This RFC (or potentially [Always Require Braces]; I think the content of [RFC PR 147] shifted over time, so it is hard to interpret the "+1" comments now). * "I find `let s = S0;` jarring, think its an enum initially." ==> Favors: Always Require Braces * "Frequently start out with an empty struct and add fields as I need them." ==> Favors: This RFC or Always Require Braces * "Foo{} suggests is constructing something that it's not; all uses of the value `Foo` are indistinguishable from each other" ==> Favors: Status Quo From d569e5af72bc4703bfe06f34561abe84238c8340 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 30 Sep 2014 20:53:03 +0200 Subject: [PATCH 13/14] explicitly refer back to paragraph that defines the enum workaround. --- active/0000-empty-structs-with-braces.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 512b410e6a5..5ba2b9615b7 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -109,7 +109,8 @@ author (pnkfelix) would be happy to take this tack.) The main reason not to take this tack is that some people may like writing empty structs without braces, but do not want to switch to the -unary enum version. See "I wouldn't want to force noisier syntax ..." +unary enum version described in the previous paragraph. +See "I wouldn't want to force noisier syntax ..." in the [Recent History] appendix. ## Status quo From f44ac712b86e6c4395df2c48a8649fa259ab4fff Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 7 Oct 2014 13:48:02 +0200 Subject: [PATCH 14/14] Update to deal with aforementioned type- vs value- namespace issue. --- active/0000-empty-structs-with-braces.md | 79 +++++++++++++++++------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/active/0000-empty-structs-with-braces.md b/active/0000-empty-structs-with-braces.md index 5ba2b9615b7..4e2f55d8d04 100644 --- a/active/0000-empty-structs-with-braces.md +++ b/active/0000-empty-structs-with-braces.md @@ -5,9 +5,11 @@ # Summary When a struct type `S` has no fields (a so-called "empty struct"), -allow it to be defined via either `struct S;` or `struct S {}`, and -allow instances of it to be constructed and pattern-matched via either -`S` or `S {}`. +allow it to be defined via either `struct S;` or `struct S {}`. +When defined via `struct S;`, allow instances of it to be constructed +and pattern-matched via either `S` or `S {}`. +When defined via `struct S {}`, require instances to be constructed +and pattern-matched solely via `S {}`. # Motivation @@ -46,7 +48,7 @@ and non-empty, and the associated revisions of changing removing and adding curly braces is aggravating (both in effort revising the code, and also in extra noise introduced into commit histories). -This RFC proposes going back to the state we were in circa February +This RFC proposes an approach similar to the one we used circa February 2013, when both `S0` and `S0 { }` were accepted syntaxes for an empty struct. The parsing ambiguity that motivated removing support for `S0 { }` is no longer present (see the [Ancient History] appendix). @@ -55,34 +57,43 @@ in the language now. # Detailed design - * Allow `S` to be defined via either `struct S;` (as today) - or `struct S {}` ("new") +There are two kinds of empty structs: Braced empty structs and +flexible empty structs. Flexible empty structs are a slight +generalization of the structs that we have today. - * Allow instances of `S` to be constructed via either the - expression `S` (as today) or the expression `S {}` ("new") +Flexible empty structs are defined via the syntax `struct S;` (as today). - * Allow instances of `S` to be pattern matched via either the - pattern `S` (as today) or the pattern `S {}` ("new"). +Braced empty structs are defined via the syntax `struct S { }` ("new"). -Revise the grammar of struct item definitions so that one can write -either `struct S;` or `struct S { }`. The two forms are synonymous. -The first is preferred with respect to coding style; for example, the -first is emitted by the pretty printer. +Both braced and flexible empty structs can be constructed via the +expression syntax `S { }` ("new"). Flexible empty structs, as today, +can also be constructed via the expression syntax `S`. -Revise the grammar of expressions and patterns so that, when `S` is an -empty struct, one can write either `S` or `S { }`. The two forms are -synonymous. Again, the first is preferred with respect to coding -style, and is emitted by the pretty printer. +Both braced and flexible empty structs can be pattern-matched via the +pattern syntax `S { }` ("new"). Flexible empty structs, as today, +can also be pattern-matched via the pattern syntax `S`. -The format of the definiton has no bearing on the format of the -expressions or pattern forms; either syntax can be used for any -empty-struct, regardless of how it is defined. +Braced empty struct definitions solely affect the type namespace, +just like normal non-empty structs. +Flexible empty structs affect both the type and value namespaces. + +As a matter of style, using braceless syntax is preferred for +constructing and pattern-matching flexible empty structs. For +example, pretty-printer tools are encouraged to emit braceless forms +if they know that the corresponding struct is a flexible empty struct. +(Note that pretty printers that handle incomplete fragments may not +have such information available.) There is no ambiguity introduced by this change, because we have already introduced a restriction to the Rust grammar to force the use of parentheses to disambiguate struct literals in such contexts. (See [Rust RFC 25]). +The expectation is that when migrating code from a flexible empty +struct to a non-empty struct, it can start by first migrating to a +braced empty struct (and then have a tool indicate all of the +locations where braces need to be added); after that step has been +completed, one can then take the next step of adding the actual field. # Drawbacks @@ -120,6 +131,32 @@ need to handle empty structs as a special case. We may continue hitting bugs like [CFG parse bug]. Some users will be annoyed but most will probably cope. +## Synonymous in all contexts + +Alternative 3: An earlier version of this RFC proposed having `struct +S;` be entirely synonymous with `struct S { }`, and the expression +`S { }` be synonymous with `S`. + +This was deemed problematic, since it would mean that `S { }` would +put an entry into both the type and value namespaces, while +`S { x: int }` would only put an entry into the type namespace. +Thus the current draft of the RFC proposes the "flexible" versus +"braced" distinction for empty structs. + +## Never synonymous + +Alternative 4: Treat `struct S;` as requiring `S` at the expression +and pattern sites, and `struct S { }` as requiring `S { }` at the +expression and pattern sites. + +This in some ways follows a principle of least surprise, but it also +is really hard to justify having both syntaxes available for empty +structs with no flexibility about how they are used. (Note again that +one would have the option of choosing between +`enum S { S }`, `struct S;`, or `struct S { }`, each with their own +idiosyncrasies about whether you have to write `S` or `S { }`.) +I would rather adopt "Always Require Braces" than "Never Synonymous" + ## Empty Tuple Structs One might say "why are you including support for curly braces, but not