{"action":"created","comment":{"url":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments/230586322","pull_request_review_id":171359958,"id":230586322,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzMDU4NjMyMg==","diff_hunk":"@@ -0,0 +1,1262 @@\n+- Feature Name: `stuctural_records`\n+- Start Date: 2018-11-02\n+- RFC PR:\n+- Rust Issue:\n+\n+# Summary\n+[summary]: #summary\n+\n+**Introduce _structural records_** of the form `{ foo: 1u8, bar: true }` of\n+type `{ foo: u8, bar: bool }` into the language. Another way to understand\n+these sorts of objects is to think of them as *\"tuples with named fields\"*,\n+*\"unnamed structs\"*, or *\"anonymous structs\"*.\n+\n+# Motivation\n+[motivation]: #motivation\n+\n+There are four motivations to introduce structural records,\n+two major and two minor.\n+\n+## Major: Improving ergonomics, readability, and maintainability\n+\n+Sometimes, you just need a one-off struct that you will use a few times for\n+some public API or in particular for some internal API. For example, you'd like\n+to send over a few values to a function, but you don't want to have too many\n+function arguments. In such a scenario, defining a new nominal `struct` which\n+contains all the fields you want to pass is not a particularly ergonomic solution.\n+Instead, it is rather heavy weight:\n+\n+```rust\n+struct Color {\n+    red: u8,\n+    green: u8,\n+    blue: u8,\n+}\n+\n+fn caller() {\n+    ...\n+    let color = Color { red: 255, green: 0, blue: 0 };\n+    ...\n+    do_stuff_with(color);\n+    ...\n+}\n+\n+fn do_stuff_with(color: Color) { // Only time we use `Color`!\n+    some_stuff(color.red);\n+    ...\n+    other_stuff(color.green);\n+    ...\n+    yet_more_stuff(color.blue);\n+}\n+```\n+\n+To remedy the ergonomics problem, you may instead opt for using a positional\n+tuple to contain the values you want to pass. However, now you have likely\n+created a regression for readers of your code since the fields of tuples are\n+accessed with their positional index, which does not carry clear semantic intent:\n+\n+```rust\n+fn caller() {\n+    ...\n+    let color = (255, 0, 0);\n+    ...\n+    do_stuff_with(color); // Unclear what each position means...\n+    ...\n+}\n+\n+fn do_stuff_with(color: (u8, u8, u8)) { // More ergonomic!\n+    some_stuff(color.0); // But less readable... :(\n+    ...\n+    other_stuff(color.1);\n+    ...\n+    yet_more_stuff(color.2);\n+}\n+```\n+\n+Using structural records, we can have our cake and eat it too:\n+\n+```rust\n+fn caller() {\n+    ...\n+    let color = { red: 255, green: 0, blue: 0 };\n+    ...\n+    do_stuff_with(color); // ...but here it is clear.\n+    ...\n+}\n+\n+fn do_stuff_with(color: { red: u8, green: u8, blue: u8 }) { // More ergonomic!\n+    some_stuff(color.red); // *And* readable! :)\n+    ...\n+    other_stuff(color.green);\n+    ...\n+    yet_more_stuff(color.blue);\n+}\n+```\n+\n+In the above snippet, the semantic intent of the fields is clear both when\n+reading the body of the function, as well as when reading the documentation\n+when `do_stuff_with` is exposed as a public API.\n+\n+[@eternaleye]: https://internals.rust-lang.org/t/pre-rfc-unnamed-struct-types/3872/58\n+[@kardeiz]: https://internals.rust-lang.org/t/pre-rfc-unnamed-struct-types/3872/65\n+\n+Another example of reducing boilerplate was given by [@eternaleye]:\n+\n+```rust\n+struct RectangleClassic {\n+    width: u64,\n+    height u64,\n+    red: u8,\n+    green: u8,\n+    blue: u8,\n+}\n+\n+struct RectangleTidy {\n+    dimensions: {\n+        width: u64,\n+        height: u64,\n+    },\n+    color: {\n+        red: u8,\n+        green: u8,\n+        blue: u8,\n+    },\n+}\n+```\n+\n+In the second type `RectangleTidy`, we keep boilerplate to a minimum and we can\n+also treat `rect.color` and `rect.dimensions` as separate objects and move them\n+out of `rect : RectangleTidy` as units, which we cannot do with `RectangleClassic`.\n+If we wanted to do that, we would have to invent two new types `Dimensions` and\n+`Color` and then `#[derive(..)]` the bits and pieces that we need.\n+As noted by [@kardeiz], this ability may also be useful for serializing one-off\n+structures with `serde`.\n+\n+## Major: Better rapid prototyping and refactoring\n+\n+Let's assume we opted for using the type `{ red: u8, green: u8, blue: u8 }`\n+from above. This gave us the ability to prototype our application rapidly.\n+However, as time passes, we might have more uses for RGB colours and so we\n+decide to make a nominal type out of it and give it some operations.\n+Because the structural record we've used above has named fields, we can easily\n+refactor it into the nominal type `Color`.\n+\n+Indeed, an IDE should be able to use the information that exists and provide\n+the refactoring for you automatically. If we had instead used a positional tuple,\n+the information would simply be unavailable. Thus, the refactoring could not be\n+made automatically and if you had to do it manually, you would need to spend\n+time understanding the code to deduce what proper field names would be.\n+\n+## Minor: Emulating named function arguments\n+\n+Structural records could be considered to lessen the need for named\n+function arguments by writing in the following style:\n+\n+```rust\n+fn foo(bar: { baz: u8, quux: u8 }) -> u8 {\n+    bar.baz + bar.quux\n+}\n+\n+fn main() {\n+    assert_eq!(3, foo({ baz: 1, quux: 2 }));\n+}\n+```\n+\n+While this is a possible use case, in this RFC, we do not see named function\n+arguments as a *major* motivation for structural records as they do not cover\n+aspects of named arguments that people sometimes want. In particular:\n+\n+1. With structural records, you cannot construct them positionally.\n+   In other words, you may not call `foo` from above with `foo(1, 2)` because\n+   these records do not have a defined order that users can make use of.\n+\n+[`<*const T>::copy_to_nonoverlapping`]: \n+https://doc.rust-lang.org/std/primitive.pointer.html#method.copy_to_nonoverlapping\n+\n+2. You cannot adapt existing standard library functions to use named arguments.\n+   Consider for example the function [`<*const T>::copy_to_nonoverlapping`].\n+   It has the following signature:\n+\n+   ```rust\n+   pub unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize);\n+   ```\n+\n+   Because this is an `unsafe` function, we might want to call this as:\n+\n+   ```rust\n+   ptr.copy_to_nonoverlapping({ dest: <the_destination>, count: <the_count> })\n+   ```\n+\n+   However, because we can write:\n+\n+   ```rust\n+   const X: unsafe fn(*const u8, *mut u8, usize)\n+          = <*const u8>::copy_to_nonoverlapping;\n+   ```\n+\n+   it would be a breaking change to redefine the function to take a structural\n+   record instead of two arguments.\n+\n+Having noted these two possible deficiencies of structural records as a way to\n+emulate named function arguments, this emulation can still work well in many\n+cases. Thus, while the motivation is not major here, we still consider it to\n+be a minor motivation.\n+\n+## Minor: Smoothing out the language\n+\n+The current situation in the language with respect to product types can be\n+described with the following table:\n+\n+|                  | Nominal                         | Structural        |\n+|------------------|---------------------------------|-------------------|\n+| **Unit**         | Yes, `struct T;`                | Yes, `()`         |\n+| **Positional**   | Yes, `struct T(A, B);`          | Yes, `(A, B)`     |\n+| **Named fields** | Yes, `struct T { a: A, b: B }`  | **No**, this RFC  |\n+\n+As we can see, the current situation is inconsistent.\n+While the language provides for unit types and positional product types\n+of both the nominal and structural flavour, the structural variant of\n+structs with named fields is missing while the nominal type exists.\n+A consistent programming language is a beautiful language, but it's not an\n+end in itself. Instead, the main benefit is to reduce surprises for learners.\n+\n+[@withoutboats]: https://internals.rust-lang.org/t/pre-rfc-unnamed-struct-types/3872/23\n+[@regexident]: https://internals.rust-lang.org/t/pre-rfc-catching-functions/6505/188\n+\n+Indeed, [@withoutboats] noted:\n+\n+> To me this seems consistent and fine - the kind of feature a user could infer to exist from knowing the other features of Rust - but I’m not thrilled by the idea of trying to use this to implement named arguments.\n+\n+and [@regexident] noted:\n+\n+> An unnamed type can have zero, n indexed or n named members:\n+> + `()`\n+> + `(T, U, …)`\n+> + **`{ t: T, u: U }`** [*editor's note:* this is **_not_** supported *yet*.]\n+\n+# Guide-level explanation\n+[guide-level-explanation]: #guide-level-explanation\n+\n+## Vocabulary\n+\n+[structural typing]: https://en.wikipedia.org/wiki/Structural_type_system\n+[nominal typing]: https://en.wikipedia.org/wiki/Nominal_type_system\n+[product type]: https://en.wikipedia.org/wiki/Product_type\n+[record type]: https://en.wikipedia.org/wiki/Record_(computer_science)\n+\n+- With *[structural typing]*, the equivalence of two types is determined by\n+  looking at their structure. For example, given the type `(A, B)` and `(C, D)`\n+  where `A`, `B`, `C`, and `D` are types, if `A == C` and `B == D`,\n+  then `(A, B) == (C, D)`. In Rust, the only case of structural typing is\n+  the positional tuples we've already seen.\n+\n+  The main benefit of structural typing is that it becomes ergonomic and\n+  quite expressive to produce new types *\"out of thin air\"* without having\n+  to predefine each type.\n+\n+- With *[nominal typing]*, types are instead equivalent if their declared\n+  names are the same. For example, assuming that the types `Foo` and `Bar`\n+  are defined as:\n+\n+  ```rust\n+  struct Foo(u8, bool);\n+  struct Bar(u8, bool);\n+  ```\n+\n+  even though they have the same structure, `Foo` is not the same type as `Bar`\n+  and so the type system will not let you use a `Foo` where a `Bar` is expected.\n+\n+  The main benefit to nominal typing is maintainability and robustness.\n+  In Rust, we take advantage of nominal typing coupled with privacy to\n+  enforce API invariants. This is particularly important for code that\n+  involves use of `unsafe`.\n+\n+- A *[product type]* is a type which is made up of a sequence of other types.\n+  For example, the type `(A, B, C)` consists of the types `A`, `B`, *and* `C`.\n+  They are called product types because the number of possible values that the\n+  type can take is the *product* of the number of values that each component /\n+  factor / operand type can take. For example, if we consider `(A, B, C)`,\n+  the number of values is `values(A) * values(B) * values(C)`.\n+\n+- A *[record type]* is a special case of a product type in which each component\n+  type is *labeled*. In Rust, record types are `struct`s with named fields.\n+  For example, you can write:\n+\n+  ```rust\n+  struct Foo {\n+      bar: u8,\n+      baz: bool,\n+  }\n+  ```\n+\n+  The only case of a record type in Rust uses nominal typing.\n+  There is currently no structural variant of record types.\n+  This brings us to the proposal in this RFC...\n+\n+## Proposal\n+\n+As we've seen, Rust currently lacks a structurally typed variant of record types.\n+In this RFC, we propose to change this by introducing record types that are\n+also structural, or in other words: *\"structural records\"*.\n+\n+### Construction\n+\n+To create a structural record with the field `alpha` with value `42u8`,\n+`beta` with value `true`, and `gamma` with value `\"Dancing Ferris\"`,\n+you can simply write:\n+\n+```rust\n+let my_record = {\n+    alpha: 42u8,\n+    beta: true,\n+    gamma: \"Dancing Ferris\"\n+};\n+```\n+\n+Note how this is the same syntax as used for creating normal structs but without\n+the name of the struct. So we have taken:\n+\n+```rust\n+struct MyRecordType {\n+    alpha: u8,\n+    beta: bool,\n+    gamma: &'static str,\n+}\n+\n+let my_record_nominal = MyRecordType {\n+    alpha: 42u8,\n+    beta: true,\n+    gamma: \"Dancing Ferris\"\n+};\n+```\n+\n+and removed `MyRecordType`. Note that because we are using structural typing,\n+we did not have to define a type `MyRecordType` ahead of time.\n+\n+If you already had variables named `alpha` and `beta` in scope,\n+just as you could have with `MyRecordType`, you could have also written:\n+\n+```rust\n+let alpha = 42u8;\n+let beta = true;\n+\n+let my_record = {\n+    alpha,\n+    beta,\n+    gamma: \"Dancing Ferris\"\n+};\n+```\n+\n+### Pattern matching\n+\n+Once you have produced a structural record, you can also pattern match on the\n+expression. To do so, you can write:\n+\n+```rust\n+match my_record {\n+    { alpha, beta, gamma } => println!(\"{}, {}, {}\", alpha, beta, gamma),\n+}\n+```\n+\n+This pattern is *irrefutable* so you can also just write:\n+\n+```rust\n+let { alpha, beta, gamma } = my_record;\n+println!(\"{}, {}, {}\", alpha, beta, gamma);\n+```\n+\n+This is not particular to `match` and `let`. This also works for `if let`,\n+`while let`, `for` loops, and function arguments.\n+\n+If we had used `MyRecordType`, you would have instead written:\n+\n+```rust\n+let MyRecordType { alpha, beta, gamma } = my_record_nominal;\n+println!(\"{}, {}, {}\", alpha, beta, gamma);\n+```\n+\n+When pattern matching on a structural record, it is also possible to give\n+the binding you've created a different name. To do so, write:\n+\n+```rust\n+let { alpha: new_alpha, beta, gamma } = my_record;\n+println!(\"{}, {}, {}\", new_alpha, beta, gamma);\n+```\n+\n+In this snippet, we've bound the field `alpha` to the binding `new_alpha`.\n+This is not limited to one field, you can do this will all of them.\n+\n+As with nominal structs, it's possible to ignore some or all fields when\n+pattern matching on structural records. Examples include:\n+\n+```rust\n+let { alpha, .. } = my_record;\n+println!(\"{}\", alpha);\n+\n+let { .. } = my_record;\n+```\n+\n+### Field access\n+\n+Given the binding `my_record_nominal` of type `MyRecordType`, you can access its\n+fields with the usual `my_record_nomina.alpha` syntax. This also applies to\n+structural records. It is perfectly valid to move or copy:\n+\n+```rust\n+println!(\"The answer to life... is: {}\", my_record.alpha);\n+```\n+\n+or to borrow a field:\n+\n+```rust\n+fn borrow(x: &bool) { .. }\n+\n+borrow(&my_record.beta);\n+```\n+\n+including mutably:\n+\n+```rust\n+fn mutably_borrow(x: &mut bool) { .. }\n+\n+let mut my_record = { alpha: 42u8, beta: true, gamma: \"Dancing Ferris\" };\n+\n+mutably_borrow(&mut my_record.beta);\n+```\n+\n+### One-field records\n+\n+Positional tuples with a single element use the syntax `(x,)` both for types\n+and for values to distinguish between the 1-element tuple and wrapping a value\n+in parenthesis. To avoid ambiguities with the `{ x }` syntax that is already\n+allowed, we impose the same rule for one-field structural records. To create\n+such a record, you must write `{ x, }` or `{ x: <value>, }`. This difference\n+is also imposed to maintain a sense of consistency between the positional\n+and the non-positional structural variant.\n+\n+### Struct update syntax\n+\n+Nominal structs support what is referred to as the *\"struct update syntax\"*,\n+otherwise known as functional record update (FRU). For example, you can write:\n+\n+```rust\n+struct Color {\n+    red: u8,\n+    green: u8,\n+    blue: u8,\n+}\n+\n+let yellow = Color { red: 0, green: 255, blue: 255 };\n+\n+let white = Color { red: 255, ..yellow };\n+```\n+\n+This also works for structural records, so you can write:\n+\n+```rust\n+let yellow = { red: 0, green: 255, blue: 255 };\n+\n+let white = { red: 255, ..yellow };\n+```\n+\n+To match the behaviour of FRU for nominal structs, we impose a restriction\n+that the fields mentioned before `..yellow` must all exist in `yellow` and\n+have the same types. This means that we cannot write:\n+\n+```rust\n+let white_50_opacity = { alpha: 0.5, red: 255, ..yellow };\n+```\n+\n+However, there is no fundamental reason why we could not allow this,\n+but to start off more conservative and to be uniform in behaviour,\n+we don't allow this for the time being.\n+\n+### The type of a structural record\n+\n+You might be wondering what the type of `my_record` that we've been using\n+thus far is. Because this is structural typing, the fields are significant,\n+so the type of the record is simply:\n+\n+```rust\n+type TheRecord = { alpha: u8, beta: bool, gamma: &'static str };\n+\n+let my_record: TheRecord = { alpha: 42u8, beta: true, gamma: \"Dancing Ferris\" };\n+```\n+\n+Notice how this matches the way we defined `MyRecordType` is we remove\n+the prefix `struct MyRecordType`.\n+\n+The order in which we've put `alpha`, `beta`, and `gamma` here does not matter.\n+We could have also written:\n+\n+```rust\n+type TheRecord = { beta: bool, alpha: u8, gamma: &'static str };\n+```\n+\n+As long as the type is a *permutation* of each pair of field name and field type,\n+the type is the same. This also means that we can write: \n+\n+```rust\n+let my_record: TheRecord = { alpha: 42u8, gamma: \"Dancing Ferris\", beta: true };\n+```\n+\n+### Implemented traits\n+\n+With respect to trait implementations, because the type is structural,\n+and because there may be an unbound number of fields that can all be arbitrary\n+identifiers, there's no way to define implementations for the usual traits\n+in the language itself.\n+\n+[tuples]: https://doc.rust-lang.org/std/primitive.tuple.html\n+\n+Instead, the compiler will automatically provide trait implementations for the\n+standard traits that are implemented for [tuples]. These traits are: `Clone`,\n+`Copy`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Debug`, `Default`, and `Hash`.\n+Each of these traits will only be implemented if all the field types of a struct\n+implements the trait.\n+\n+For all of the aforementioned standard traits, the semantics of the\n+implementations are similar to that of `#[derive(Trait)]` for named-field structs.\n+\n++ For cloning `{ alpha: A, beta: B, gamma: C }` the logic is simply:\n+\n+  ```rust\n+  {\n+      alpha: self.alpha.clone(),\n+      beta: self.beta.clone(),\n+      gamma: self.gamma.clone(),\n+  }\n+  ```\n+\n++ For `Default`, you would get:\n+\n+  ```rust\n+  {\n+      alpha: Default::default(),\n+      beta: Default::default(),\n+      gamma: Default::default(),\n+  }\n+  ```\n+\n++ For `PartialEq`, each field is compared with same field in `other: Self`.\n+\n++ For `ParialOrd` and `Ord`, lexicographic ordering is used based on\n+  the name of the fields and not the order given because structural records\n+  don't respect the order in which the fields are put when constructing or\n+  giving the type of the record.\n+\n++ For `Debug` the same lexicographic ordering for `Ord` is used.\n+  As an example, when printing out `my_record` as with:\n+\n+  ```rust\n+  let my_record = { beta: true, alpha: 42u8, gamma: \"Dancing Ferris\" };\n+  println!(\"{:#?}\", my_record);\n+  ```\n+  \n+  the following would appear:\n+  \n+  ```rust\n+  {\n+      alpha: 42,\n+      beta: true,\n+      gamma: \"Dancing Ferris\"\n+  }\n+  ```\n+\n++ For `Hash`, the same ordering of the fields as before is used and then\n+  `self.the_field.hash(state)` is called on each field in that order.\n+  For example:\n+\n+  ```rust\n+  self.alpha.hash(state);\n+  self.beta.hash(state);\n+  self.gamma.hash(state);\n+  ```\n+\n+For auto traits (e.g. `Send`, `Sync`, `Unpin`), if all field types implement\n+the auto trait, then the structural record does as well.\n+For example, if `A` and `B` are `Send`, then so is `{ x: A, y: B }`.\n+\n+A structural record is `Sized` if all field types are.\n+If the lexicographically last field of a structural record is `!Sized`,\n+then so is the structural record. If any other field but the last is\n+`!Sized`, then the type of the structural record is not well-formed.\n+\n+### Implementations and orphans\n+\n+[RFC 2451]: https://github.com/rust-lang/rfcs/pull/2451\n+\n+It is possible to define your own implementations for a structural record.\n+The orphan rules that apply here are those of [RFC 2451] by viewing a structural\n+record as a positional tuple after sorting the elements lexicographically.\n+\n+For example, if a `trait` is crate-local, we may implement it for a record:\n+\n+```rust\n+trait Foo {}\n+\n+impl Foo for { alpha: bool, beta: u8 } {}\n+```\n+\n+Under 2451, we can also write:\n+\n+```rust\n+struct Local<T>(T);\n+\n+impl From<()> for Local<{ alpha: bool, beta: u8 }> { ... }\n+```\n+\n+This is valid because `Local` is considered a local type.\n+\n+However, a structural record itself isn't a local type, so you cannot write:\n+\n+```rust\n+struct A;\n+\n+impl From<()> for { alpha: A, beta: u8 } { ... }\n+```\n+\n+This is the case even though `A` is a type local to the crate.\n+\n+The behaviour for inherent implementations is also akin to tuples.\n+\n+# Reference-level explanation\n+[reference-level-explanation]: #reference-level-explanation\n+\n+### Grammar\n+[grammar]: #grammar\n+\n+Given:\n+\n+```rust\n+field ::= ident | LIT_INTEGER ;\n+```\n+\n+We extend the expression grammar with:\n+\n+```rust\n+expr ::= ... | expr_srec ;\n+\n+expr_srec ::= \"{\" field_init \",\" field_inits_tail? \"}\" ;\n+field_inits_tail ::= field_inits \",\"? | (field_inits \",\")? DOTDOT expr ;\n+field_inits ::= field_init (\",\" field_init)* ;\n+field_init ::= ident | field \":\" expr ;\n+```\n+\n+Note that this grammar permits `{ 0: x, 1: y }`.\n+We do so to increase reuse in parsing, to improve consistency,\n+and to simplify for macro users.\n+This is also applied in the grammars below.\n+\n+We extend the pattern grammar with:\n+\n+```rust\n+pat ::= ... | pat_srec ;\n+\n+pat_srec ::= \"{\" pat_srec_start \"}\" ;\n+pat_srec_start ::= DOTDOT | pat_field \",\" pat_srec_tail\n+pat_srec_tail ::= pat_fields \",\"? | (pat_fields \",\")? DOTDOT? ;\n+pat_fields ::= pat_field (\",\" pat_field)* ;\n+pat_field ::= BOX? binding_mode? ident | field \":\" pat ;\n+```\n+\n+Finally, we extend the type grammar with:\n+\n+```rust\n+ty ::= ... | ty_srec ;\n+\n+ty_srec ::= \"{\" (ty_field \",\")+ (ty_field \",\"?)? \"}\" ;","path":"text/0000-structural-records.md","position":668,"original_position":668,"commit_id":"b07065533df8e2d837ea8b8fb31e15fff5540335","original_commit_id":"051686558a1b57448193fbc2938f1a3f193e9c7e","user":{"login":"Centril","id":855702,"node_id":"MDQ6VXNlcjg1NTcwMg==","avatar_url":"https://avatars2.githubusercontent.com/u/855702?v=4","gravatar_id":"","url":"https://api.github.com/users/Centril","html_url":"https://github.com/Centril","followers_url":"https://api.github.com/users/Centril/followers","following_url":"https://api.github.com/users/Centril/following{/other_user}","gists_url":"https://api.github.com/users/Centril/gists{/gist_id}","starred_url":"https://api.github.com/users/Centril/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Centril/subscriptions","organizations_url":"https://api.github.com/users/Centril/orgs","repos_url":"https://api.github.com/users/Centril/repos","events_url":"https://api.github.com/users/Centril/events{/privacy}","received_events_url":"https://api.github.com/users/Centril/received_events","type":"User","site_admin":false},"body":"@H2CO3 oh sure; the parser must handle it, but the perils of backtracking is that it would considerably slow down parsing or produce bad error messages. What I'm saying is that the code paths in the parser that would handle this are pathological, so the slow downs are unlikely.\r\n\r\nThere's also no *ambiguity* here at all, but lookahead / backtracking / GLL may be required.\r\n\r\nIf we ever use the [GLL crate](https://github.com/lykenware/gll/) in the compiler then we don't even need backtracking... it provides a correct algorithm to parse context-free-grammars and can produce good error messages for these sort of things because it produces a parse forest that you can catch certain patterns on.","created_at":"2018-11-04T12:08:43Z","updated_at":"2018-11-04T12:08:43Z","html_url":"https://github.com/rust-lang/rfcs/pull/2584#discussion_r230586322","pull_request_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/2584","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments/230586322"},"html":{"href":"https://github.com/rust-lang/rfcs/pull/2584#discussion_r230586322"},"pull_request":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/2584"}},"in_reply_to_id":230526616},"pull_request":{"url":"https://api.github.com/repos/rust-lang/rfcs/pulls/2584","id":228055036,"node_id":"MDExOlB1bGxSZXF1ZXN0MjI4MDU1MDM2","html_url":"https://github.com/rust-lang/rfcs/pull/2584","diff_url":"https://github.com/rust-lang/rfcs/pull/2584.diff","patch_url":"https://github.com/rust-lang/rfcs/pull/2584.patch","issue_url":"https://api.github.com/repos/rust-lang/rfcs/issues/2584","number":2584,"state":"open","locked":false,"title":"RFC: Structural Records","user":{"login":"Centril","id":855702,"node_id":"MDQ6VXNlcjg1NTcwMg==","avatar_url":"https://avatars2.githubusercontent.com/u/855702?v=4","gravatar_id":"","url":"https://api.github.com/users/Centril","html_url":"https://github.com/Centril","followers_url":"https://api.github.com/users/Centril/followers","following_url":"https://api.github.com/users/Centril/following{/other_user}","gists_url":"https://api.github.com/users/Centril/gists{/gist_id}","starred_url":"https://api.github.com/users/Centril/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Centril/subscriptions","organizations_url":"https://api.github.com/users/Centril/orgs","repos_url":"https://api.github.com/users/Centril/repos","events_url":"https://api.github.com/users/Centril/events{/privacy}","received_events_url":"https://api.github.com/users/Centril/received_events","type":"User","site_admin":false},"body":"### [🖼️ Rendered](https://github.com/Centril/rfcs/blob/rfc/structural-records/text/0000-structural-records.md)\r\n\r\n### 📝 Summary\r\n\r\n**Introduce _structural records_** of the form `{ foo: 1u8, bar: true }` of type `{ foo: u8, bar: bool }` into the language. Another way to understand these sorts of objects is to think of them as *\"tuples with named fields\"*, *\"unnamed structs\"*, or *\"anonymous structs\"*.\r\n\r\n### 💖 Thanks\r\n\r\nTo @kennytm, @alexreg, @Nemo157, and @tinaun for reviewing the draft version of this RFC.\r\nTo @varkor and @pnkfelix for good and helpful discussions.","created_at":"2018-11-02T20:47:28Z","updated_at":"2018-11-04T12:08:43Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9a9c4bfea292633b8b9fdfd9aa1b9f72f227313e","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":211667886,"node_id":"MDU6TGFiZWwyMTE2Njc4ODY=","url":"https://api.github.com/repos/rust-lang/rfcs/labels/T-lang","name":"T-lang","color":"bfd4f2","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/2584/commits","review_comments_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/2584/comments","review_comment_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rust-lang/rfcs/issues/2584/comments","statuses_url":"https://api.github.com/repos/rust-lang/rfcs/statuses/b07065533df8e2d837ea8b8fb31e15fff5540335","head":{"label":"Centril:rfc/structural-records","ref":"rfc/structural-records","sha":"b07065533df8e2d837ea8b8fb31e15fff5540335","user":{"login":"Centril","id":855702,"node_id":"MDQ6VXNlcjg1NTcwMg==","avatar_url":"https://avatars2.githubusercontent.com/u/855702?v=4","gravatar_id":"","url":"https://api.github.com/users/Centril","html_url":"https://github.com/Centril","followers_url":"https://api.github.com/users/Centril/followers","following_url":"https://api.github.com/users/Centril/following{/other_user}","gists_url":"https://api.github.com/users/Centril/gists{/gist_id}","starred_url":"https://api.github.com/users/Centril/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Centril/subscriptions","organizations_url":"https://api.github.com/users/Centril/orgs","repos_url":"https://api.github.com/users/Centril/repos","events_url":"https://api.github.com/users/Centril/events{/privacy}","received_events_url":"https://api.github.com/users/Centril/received_events","type":"User","site_admin":false},"repo":{"id":103166643,"node_id":"MDEwOlJlcG9zaXRvcnkxMDMxNjY2NDM=","name":"rfcs","full_name":"Centril/rfcs","private":false,"owner":{"login":"Centril","id":855702,"node_id":"MDQ6VXNlcjg1NTcwMg==","avatar_url":"https://avatars2.githubusercontent.com/u/855702?v=4","gravatar_id":"","url":"https://api.github.com/users/Centril","html_url":"https://github.com/Centril","followers_url":"https://api.github.com/users/Centril/followers","following_url":"https://api.github.com/users/Centril/following{/other_user}","gists_url":"https://api.github.com/users/Centril/gists{/gist_id}","starred_url":"https://api.github.com/users/Centril/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Centril/subscriptions","organizations_url":"https://api.github.com/users/Centril/orgs","repos_url":"https://api.github.com/users/Centril/repos","events_url":"https://api.github.com/users/Centril/events{/privacy}","received_events_url":"https://api.github.com/users/Centril/received_events","type":"User","site_admin":false},"html_url":"https://github.com/Centril/rfcs","description":"RFCs for changes to Rust","fork":true,"url":"https://api.github.com/repos/Centril/rfcs","forks_url":"https://api.github.com/repos/Centril/rfcs/forks","keys_url":"https://api.github.com/repos/Centril/rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Centril/rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Centril/rfcs/teams","hooks_url":"https://api.github.com/repos/Centril/rfcs/hooks","issue_events_url":"https://api.github.com/repos/Centril/rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/Centril/rfcs/events","assignees_url":"https://api.github.com/repos/Centril/rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/Centril/rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/Centril/rfcs/tags","blobs_url":"https://api.github.com/repos/Centril/rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Centril/rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Centril/rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/Centril/rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Centril/rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/Centril/rfcs/languages","stargazers_url":"https://api.github.com/repos/Centril/rfcs/stargazers","contributors_url":"https://api.github.com/repos/Centril/rfcs/contributors","subscribers_url":"https://api.github.com/repos/Centril/rfcs/subscribers","subscription_url":"https://api.github.com/repos/Centril/rfcs/subscription","commits_url":"https://api.github.com/repos/Centril/rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/Centril/rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/Centril/rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/Centril/rfcs/issues/comments{/number}","contents_url":"https://api.github.com/repos/Centril/rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/Centril/rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Centril/rfcs/merges","archive_url":"https://api.github.com/repos/Centril/rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Centril/rfcs/downloads","issues_url":"https://api.github.com/repos/Centril/rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/Centril/rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/Centril/rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/Centril/rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Centril/rfcs/labels{/name}","releases_url":"https://api.github.com/repos/Centril/rfcs/releases{/id}","deployments_url":"https://api.github.com/repos/Centril/rfcs/deployments","created_at":"2017-09-11T17:28:49Z","updated_at":"2018-11-03T20:41:00Z","pushed_at":"2018-11-03T11:55:53Z","git_url":"git://github.com/Centril/rfcs.git","ssh_url":"git@github.com:Centril/rfcs.git","clone_url":"https://github.com/Centril/rfcs.git","svn_url":"https://github.com/Centril/rfcs","homepage":"","size":6675,"stargazers_count":2,"watchers_count":2,"language":"Shell","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":4,"mirror_url":null,"archived":false,"open_issues_count":3,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":4,"open_issues":3,"watchers":2,"default_branch":"master"}},"base":{"label":"rust-lang:master","ref":"master","sha":"859b254d2373a594085d6cdfcbcf97376905e8ae","user":{"login":"rust-lang","id":5430905,"node_id":"MDEyOk9yZ2FuaXphdGlvbjU0MzA5MDU=","avatar_url":"https://avatars1.githubusercontent.com/u/5430905?v=4","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"repo":{"id":17527989,"node_id":"MDEwOlJlcG9zaXRvcnkxNzUyNzk4OQ==","name":"rfcs","full_name":"rust-lang/rfcs","private":false,"owner":{"login":"rust-lang","id":5430905,"node_id":"MDEyOk9yZ2FuaXphdGlvbjU0MzA5MDU=","avatar_url":"https://avatars1.githubusercontent.com/u/5430905?v=4","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/rust-lang/rfcs","description":"RFCs for changes to Rust","fork":false,"url":"https://api.github.com/repos/rust-lang/rfcs","forks_url":"https://api.github.com/repos/rust-lang/rfcs/forks","keys_url":"https://api.github.com/repos/rust-lang/rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rust-lang/rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rust-lang/rfcs/teams","hooks_url":"https://api.github.com/repos/rust-lang/rfcs/hooks","issue_events_url":"https://api.github.com/repos/rust-lang/rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/rust-lang/rfcs/events","assignees_url":"https://api.github.com/repos/rust-lang/rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/rust-lang/rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/rust-lang/rfcs/tags","blobs_url":"https://api.github.com/repos/rust-lang/rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rust-lang/rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rust-lang/rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/rust-lang/rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rust-lang/rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/rust-lang/rfcs/languages","stargazers_url":"https://api.github.com/repos/rust-lang/rfcs/stargazers","contributors_url":"https://api.github.com/repos/rust-lang/rfcs/contributors","subscribers_url":"https://api.github.com/repos/rust-lang/rfcs/subscribers","subscription_url":"https://api.github.com/repos/rust-lang/rfcs/subscription","commits_url":"https://api.github.com/repos/rust-lang/rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/rust-lang/rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/rust-lang/rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/rust-lang/rfcs/issues/comments{/number}","contents_url":"https://api.github.com/repos/rust-lang/rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/rust-lang/rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rust-lang/rfcs/merges","archive_url":"https://api.github.com/repos/rust-lang/rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rust-lang/rfcs/downloads","issues_url":"https://api.github.com/repos/rust-lang/rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/rust-lang/rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/rust-lang/rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/rust-lang/rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rust-lang/rfcs/labels{/name}","releases_url":"https://api.github.com/repos/rust-lang/rfcs/releases{/id}","deployments_url":"https://api.github.com/repos/rust-lang/rfcs/deployments","created_at":"2014-03-07T21:29:00Z","updated_at":"2018-11-03T17:04:34Z","pushed_at":"2018-11-04T12:07:03Z","git_url":"git://github.com/rust-lang/rfcs.git","ssh_url":"git@github.com:rust-lang/rfcs.git","clone_url":"https://github.com/rust-lang/rfcs.git","svn_url":"https://github.com/rust-lang/rfcs","homepage":"https://rust-lang.github.io/rfcs/","size":185634,"stargazers_count":1792,"watchers_count":1792,"language":"Shell","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":705,"mirror_url":null,"archived":false,"open_issues_count":538,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":705,"open_issues":538,"watchers":1792,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/2584"},"html":{"href":"https://github.com/rust-lang/rfcs/pull/2584"},"issue":{"href":"https://api.github.com/repos/rust-lang/rfcs/issues/2584"},"comments":{"href":"https://api.github.com/repos/rust-lang/rfcs/issues/2584/comments"},"review_comments":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/2584/comments"},"review_comment":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/2584/commits"},"statuses":{"href":"https://api.github.com/repos/rust-lang/rfcs/statuses/b07065533df8e2d837ea8b8fb31e15fff5540335"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"Centril"}}