├── faq.md ├── 0000-template.md ├── README.md └── default-fields └── 0000-default-fields.md /faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ### How do I get started writing an RFC? 4 | 5 | The first step is to really understand what you are writing about. First 6 | understand the motivation - what will this let you do that you can't do today? 7 | 8 | You're likely to be extending existing parts of Rust, make sure you really 9 | understand how those parts work. Then write some example programs using the 10 | proposed feature - what are the rules for writing the feature? How would the 11 | compiler check those programs? What uses of the feature should not be allowed? 12 | How does proposed feature interact with other features? 13 | 14 | Once you understand the feature, you can start writing. I would start with the 15 | motivation section - make sure the RFC is well-motivated. Then iterate on the 16 | detailed design (making sure you address points from the motivation) and 17 | alternatives sections (I like to start early on the alternatives, since you 18 | might find one of them is better than your original idea). Finally, write the 19 | summary and other sections (I find it easier to write the summary *after* the 20 | motivation and detailed design). 21 | -------------------------------------------------------------------------------- /0000-template.md: -------------------------------------------------------------------------------- 1 | - Feature Name: (fill me in with a unique ident, my_awesome_feature) 2 | - Start Date: (fill me in with today's date, YYYY-MM-DD) 3 | - RFC PR: (leave this empty) 4 | - Rust Issue: (leave this empty) 5 | 6 | # Summary 7 | [summary]: #summary 8 | 9 | One para explanation of the feature. 10 | 11 | # Motivation 12 | [motivation]: #motivation 13 | 14 | Why are we doing this? What use cases does it support? What is the expected outcome? 15 | 16 | # Detailed design 17 | [design]: #detailed-design 18 | 19 | This is the bulk of the RFC. Explain the design in enough detail for somebody familiar 20 | with the language to understand, and for somebody familiar with the compiler to implement. 21 | This should get into specifics and corner-cases, and include examples of how the feature is used. 22 | 23 | # Drawbacks 24 | [drawbacks]: #drawbacks 25 | 26 | Why should we *not* do this? 27 | 28 | # Alternatives 29 | [alternatives]: #alternatives 30 | 31 | What other designs have been considered? What is the impact of not doing this? 32 | 33 | # Unresolved questions 34 | [unresolved]: #unresolved-questions 35 | 36 | What parts of the design are still TBD? 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RFC mentoring 2 | 3 | Interested in the [Rust RFC process](https://github.com/rust-lang/rfcs)? This is 4 | a place to improve your RFC writing skills and collaborate on writing RFCs. 5 | 6 | If you want to improve Rust, writing an RFC is an important stage in the 7 | process. RFCs are the gateway for major changes to the language, core libraries, 8 | and tools. However, writing a good RFC is difficult and can be intimidating (it 9 | can be quite a complex process to navigate and the quality bar is high). Writing 10 | an RFC should be accessible to the whole community. This repo is a step towards 11 | making that a reality. 12 | 13 | Here, you can work on real RFCs which will go through the RFC process and 14 | hopefully get accepted. We'll collaborate on RFCs and you'll get feedback and 15 | mentoring. 16 | 17 | The initial emphasis will be on language RFCs (since I believe these are the 18 | hardest for people to work on). If this is successful, I hope we can expand to 19 | other areas. 20 | 21 | Mentoring is open to anyone - you don't need to have contributed to the RFC 22 | process, compiler, or other parts of the project before. However, to get the 23 | most from this, you should know Rust pretty well, and should be interested in 24 | getting deeply involved. 25 | 26 | 27 | ## How it works 28 | 29 | We'll work on one RFC at a time, you can find the current RFC [here](https://github.com/nrc/mentor-rfcs/labels/current-rfc). 30 | There will (hopefully) be multiple people collaborating on each RFC. Once an RFC 31 | is ready for submission, we'll start work on the next one. 32 | 33 | There will be [issues](https://github.com/nrc/mentor-rfcs/issues) for mentored 34 | RFCs to work on. The issue will describe the subject of the RFC and link to any 35 | previous discussion. It will cover some of the points that should be considered 36 | and discussed in the RFC (e.g., tricky edge cases, or overlap with other 37 | potential features). An issue will be assigned to a mentor, you don't need to be 38 | assigned to work on the RFC. 39 | 40 | You should ask any questions about an RFC or the RFC process in the issue. 41 | 42 | There will be a directory for the current RFC (and for previous ones, for the 43 | curious). All work should be in that directory. You should submit drafts of 44 | parts of the RFC at a time (don't wait until you've written the whole RFC, and 45 | don't polish too much before submitting initial drafts). Send PRs for your 46 | work, you'll get most of the feedback as reviews of your PR. Feel free to give 47 | constructive feedback on other people's PRs. Don't worry about duplicating work. 48 | 49 | We'll pick the best bits from all the drafts to start the RFC. We will 50 | then collaborate (again with PRs) to improve the RFC until it is ready for 51 | submission. The mentor will then either submit the RFC into the RFC process, or 52 | ask one of the collaborators to do so. The mentor will then 53 | [shepherd](https://github.com/rust-lang/rfcs#the-role-of-the-shepherd) the RFC. 54 | Collaborators should take part in the RFC discussion and keep improving the RFC 55 | through the RFC process. 56 | 57 | 58 | ### Getting started 59 | 60 | There should be enough info on the issue to get started. See [the FAQ](faq.md) 61 | (WIP) for more general info. If you're still not sure, ask on the issue. 62 | 63 | 64 | ### Requesting an RFC for mentoring 65 | 66 | RFCs to work on in the future are [queued](https://github.com/nrc/mentor-rfcs/issues?q=is%3Aopen+is%3Aissue+label%3Aqueued-rfc). 67 | If there's one you're keen to work on, show your interest by voting with emoji. 68 | 69 | If there's an RFC that you'd like to see mentored, file a new issue (we'll label 70 | it [requested-rfc](https://github.com/nrc/mentor-rfcs/issues?q=is%3Aopen+is%3Aissue+label%3Arequested-rfc)). 71 | To be a suitable RFC for this mentoring project, it should have a good chance of 72 | being accepted (which means it should be motivated by the Rust roadmap), there 73 | should be an appropriate mentor, and it should be of general enough interest 74 | that we'd expect others to want to work on it. If it doesn't meet these 75 | criteria, but you'd like help with writing the RFC, don't hesitate to reach out 76 | to me (@nrc) or a member of the appropriate team. 77 | -------------------------------------------------------------------------------- /default-fields/0000-default-fields.md: -------------------------------------------------------------------------------- 1 | - Feature Name: (fill me in with a unique ident, my_awesome_feature) 2 | - Start Date: (fill me in with today's date, YYYY-MM-DD) 3 | - RFC PR: (leave this empty) 4 | - Rust Issue: (leave this empty) 5 | 6 | # Summary 7 | [summary]: #summary 8 | 9 | Allow `struct` definitions to supply default values for individual fields, and then allow those fields to be omitted from `struct` initialisation: 10 | 11 | ```rust 12 | struct Foo { 13 | a: &'static str, 14 | b: bool = true, 15 | c: i32, 16 | } 17 | 18 | let foo = Foo { 19 | a: "Hello", 20 | c: 42, 21 | }; 22 | ``` 23 | 24 | # Motivation 25 | [motivation]: #motivation 26 | 27 | Rust allows you to create an instance of a `struct` using a literal syntax. This requires that all fields in the `struct` are assigned a value, so it can be inconvenient for large `struct`s whose fields usually receive the same values. Literals also can't be used to initialise `struct`s with innaccessible private fields. Functional record updates can reduce noise when a `struct` derives `Default`, but are also invalid when the `struct` has private fields. 28 | 29 | To work around these shortcomings, you can create constructor functions: 30 | 31 | ```rust 32 | struct Foo { 33 | a: &'static str, 34 | b: bool, 35 | c: i32, 36 | } 37 | 38 | impl Foo { 39 | // Constructor function. 40 | fn new(a: &'static str, c: i32) -> Self { 41 | Foo { 42 | a: a, 43 | b: true, 44 | c: c 45 | } 46 | } 47 | } 48 | 49 | let foo = Foo::new("Hello", 42); 50 | ``` 51 | 52 | The problem with a constructor is that you need one for each combination of fields a caller can supply. To work around this, you can use builders, like [`process::Command`](https://doc.rust-lang.org/stable/std/process/struct.Command.html) in the standard library. Builders enable more advanced initialisation, but need additional boilerplate. 53 | 54 | This RFC proposes a solution to improve `struct` literal ergonomics, so they can be used for `struct`s with private fields, and to reduce initialisation boilerplate for simple scenarios. This is achieved by letting callers omit fields from initialisation when a default is specified for that field. This syntax also allows allows fields to be added to `struct`s in a backwards compatible way, by providing defaults for new fields. 55 | 56 | Field defaults allow a caller to initialise a `struct` with default values without needing builders or a constructor function: 57 | 58 | ```rust 59 | struct Foo { 60 | a: &'static str = "Hello", 61 | b: bool = true, 62 | c: i32 = 42, 63 | } 64 | 65 | // Overriding a single field default 66 | let foo = Foo { 67 | b: false 68 | }; 69 | 70 | // Override multiple field defaults 71 | let foo = Foo { 72 | a: "Overriden", 73 | c: 1, 74 | }; 75 | 76 | // Override no field defaults 77 | let foo = Foo {}; 78 | ``` 79 | 80 | # Detailed design 81 | [design]: #detailed-design 82 | 83 | ## Grammar 84 | 85 | In the definition of a `struct`, a default value expression can be optionally supplied for a field: 86 | 87 | ``` 88 | struct_field ::= vis? ident ':' type_path | 89 | vis? ident ':' type_path '=' expr 90 | ``` 91 | 92 | The syntax is modeled after constant expressions. Field defaults for tuple structs are not supported. 93 | 94 | ## Interpretation 95 | 96 | The value of a field default must be a compile-time expression. So any expression that's valid as a `const` can be used as a field default. This ensures values that aren't specified by the caller are deterministic and cheap to produce. A type doesn't need to derive `Default` to be valid as a field default. 97 | 98 | Valid: 99 | 100 | ```rust 101 | struct Foo { 102 | a: &'static str, 103 | b: bool = true, 104 | c: i32, 105 | } 106 | ``` 107 | 108 | Invalid: 109 | 110 | ```rust 111 | struct Foo { 112 | a: &'static str, 113 | b: Vec = Vec::new(), 114 | ^^^^^^^^^^ 115 | // error: calls in field defaults are limited to struct and enum constructors 116 | c: i32, 117 | } 118 | ``` 119 | 120 | The above error is based on `E0015` for trying to initialise a constant with a non-constant expression. As the scope of constant expressions changes this message will change too. 121 | 122 | Field defaults are like a shorthand for the 'real' initialiser, where values for missing fields are added with the supplied default expression: 123 | 124 | ```rust 125 | let foo = Foo { 126 | a: "Hello", 127 | c: 42, 128 | }; 129 | ``` 130 | 131 | is equivalent to: 132 | 133 | ```rust 134 | let foo = Foo { 135 | a: "Hello", 136 | b: true, 137 | c: 42, 138 | }; 139 | ``` 140 | 141 | The mechanism isn't exactly a shorthand because the structure can still be initialised using field defaults even if `b` is private. The caller still can't interact with private fields directly so privacy isn't violated. 142 | 143 | When a caller doesn't supply a field value during initialisation and there is no default available then the `E0063` missing field error applies. 144 | 145 | ## Order of precedence 146 | 147 | Supplied field values take precedence over field defaults: 148 | 149 | ```rust 150 | // `b` is `false`, even though the field default is `true` 151 | let foo = Foo { 152 | a: "Hello", 153 | b: false, 154 | c: 42, 155 | }; 156 | ``` 157 | 158 | Supplied field values in functional updates take precedence over field defaults: 159 | 160 | ```rust 161 | // `b` is `false`, even though the field default is `true` 162 | let foo = Foo { 163 | a: "Hello", 164 | c: 0, 165 | ..Foo { 166 | a: "Hello", 167 | b: false, 168 | c: 0 169 | } 170 | }; 171 | ``` 172 | 173 | ## Deriving `Default` 174 | 175 | When deriving `Default`, supplied field defaults are used instead of the type default. This is a feature of `#[derive(Default)]`. 176 | 177 | ```rust 178 | #[derive(Default)] 179 | struct Foo { 180 | a: &'static str, 181 | b: bool = true, 182 | c: i32, 183 | } 184 | 185 | // `b` is `true`, even though `bool::default()` is `false` 186 | let foo = Foo::default(); 187 | ``` 188 | 189 | Field defaults allow `#[derive(Default)]` to be used more widely because the types of fields with default values don't need to implement `Default`. 190 | 191 | ## Enabling backwards compatibility 192 | 193 | With no special syntax, additional fields can be added to a struct in a non-breaking fashion. Say we have the following API and consumer: 194 | 195 | ```rust 196 | mod data { 197 | pub struct Foo { 198 | pub a: &'static str, 199 | pub c: i32, 200 | } 201 | } 202 | 203 | let foo = data::Foo { 204 | a: "Hello", 205 | c: 42, 206 | } 207 | ``` 208 | 209 | We can add a new field `b` to this `struct` with a default value, and the calling code doesn't change: 210 | 211 | ```rust 212 | mod data { 213 | pub struct Foo { 214 | pub a: &'static str, 215 | pub b: bool = true, 216 | pub c: i32, 217 | } 218 | } 219 | 220 | let foo = data::Foo { 221 | a: "Hello", 222 | c: 42, 223 | } 224 | ``` 225 | 226 | By using field defaults, callers can use `struct` literals without having to know about any private fields: 227 | 228 | ```rust 229 | mod data { 230 | pub struct Foo { 231 | pub a: &'static str, 232 | pub c: i32, 233 | private_field: bool = true 234 | } 235 | } 236 | 237 | let foo = data::Foo { 238 | a: "Hello", 239 | c: 42, 240 | } 241 | ``` 242 | 243 | ## Field Privacy 244 | 245 | Default values for fields are opted into by the `struct` definition, rather than the caller initialising the structure. Field privacy doesn't need to be violated to initialise a structure. 246 | 247 | # Drawbacks 248 | [drawbacks]: #drawbacks 249 | 250 | Field defaults are limited to constant expressions. This means there are values that can't be used as defaults, so any value that requires allocation, including common collections like `Vec::new()`. It's expected that users will use a constructor function or builder for initialisers that require allocations. 251 | 252 | # Alternatives 253 | [alternatives]: #alternatives 254 | 255 | ## Allow arbitrary expressions instead of just constant expressions 256 | 257 | Allowing arbitrary expressions as field defaults would make this feature more powerful. However, limiting field defaults to constant expressions maintains the expectation that struct literals are cheap and deterministic. The same isn't true when arbitrary expressions that could reasonably panic or block on io are allowed. 258 | 259 | For complex initialisation logic, builders are the preferred option because they don't carry this same expectation. 260 | 261 | ## Allow `Default::default()` instead of just constant expressions 262 | 263 | An alternative to allowing any expression as a default value is allowing `Default::default()`, which is expected to be cheap and deterministic: 264 | 265 | ```rust 266 | struct Foo { 267 | a: &'static str, 268 | b: Vec = Vec::default(), 269 | c: i32, 270 | } 271 | 272 | let foo = Foo { 273 | a: "Hello", 274 | c: 42, 275 | } 276 | ``` 277 | 278 | It could be argued that supporting `default()` is an artificial constraint that doesn't prevent arbitrary expressions. The difference is that `Default` has an expectation of being cheap, so using it to inject logic into field initialisation is an obvious code smell. 279 | 280 | Allowing functionality to be injected into data initialisation through `default()` means struct literals may have runtime costs that aren't ever surfaced to the caller. This goes against the expectation that literal expressions have small, predictable runtime cost and are totally deterministic (and trivially predictable). 281 | 282 | ## Type inference for fields with defaults 283 | 284 | Reduce the amount of code needed to define a `struct` by allowing type inference for field defaults: 285 | 286 | ```rust 287 | struct Foo { 288 | a: &'static str, 289 | b = Bar, 290 | c: i32, 291 | } 292 | ``` 293 | 294 | This has the effect of simplifying the definition, but also requiring readers to manually work out what the type of the right-hand-side is. This could be less of an issue with IDE support. 295 | 296 | ## Explicit syntax for opting into field defaults 297 | 298 | Field defaults could require callers to use an opt-in syntax like `..`. This would make it clearer to callers that additional code could be run on struct initialisation, weakening arguments against more powerful default expressions. However it would prevent field default from being used to maintain backwards compatibility, and reduce overall ergonomics. If it's unclear whether or not a particular caller will use a default field value then its addition can't be treated as a non-breaking change. 299 | 300 | This could be worked around by adding a private field to a `struct` that does nothing, forcing a caller to opt-in to potential field defaults. 301 | 302 | The goal of the design proposed in this RFC is to let users build a struct as if its default fields weren't there. 303 | 304 | # Unresolved questions 305 | [unresolved]: #unresolved-questions 306 | --------------------------------------------------------------------------------