|
| 1 | +- Feature Name: `tuple_struct_self_ctor` |
| 2 | +- Start Date: 2017-01-18 |
| 3 | +- RFC PR: [rust-lang/rfcs#2302](https://github.com/rust-lang/rfcs/pull/2302) |
| 4 | +- Rust Issue: [rust-lang/rust#51994](https://github.com/rust-lang/rust/issues/51994) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +Tuple `struct`s can now be constructed and pattern matched with |
| 10 | +`Self(v1, v2, ..)`. A simple example: |
| 11 | + |
| 12 | +```rust |
| 13 | +struct TheAnswer(usize); |
| 14 | + |
| 15 | +impl Default for TheAnswer { |
| 16 | + fn default() -> Self { Self(42) } |
| 17 | +} |
| 18 | +``` |
| 19 | + |
| 20 | +Similarly, unit structs can also be constructed and pattern matched with `Self`. |
| 21 | + |
| 22 | +# Motivation |
| 23 | +[motivation]: #motivation |
| 24 | + |
| 25 | +This RFC proposes a consistency fix allowing `Self` to be used in more |
| 26 | +places to better match the users' intuition of the language and to get |
| 27 | +closer to feature parity between tuple structs and structs with named fields. |
| 28 | + |
| 29 | +Currently, only structs with named fields can be constructed inside |
| 30 | +impls using `Self` like so: |
| 31 | + |
| 32 | +```rust |
| 33 | +struct Mascot { name: String, age: usize } |
| 34 | + |
| 35 | +impl Default for Mascot { |
| 36 | + fn default() -> Self { |
| 37 | + Self { |
| 38 | + name: "Ferris the Crab".into(), |
| 39 | + age: 3 |
| 40 | + } |
| 41 | + } |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +while the following is not allowed: |
| 46 | + |
| 47 | +```rust |
| 48 | +struct Mascot(String, usize); |
| 49 | + |
| 50 | +impl Default for Mascot { |
| 51 | + fn default() -> Self { |
| 52 | + Self("Ferris the Crab".into(), 3) |
| 53 | + } |
| 54 | +} |
| 55 | +``` |
| 56 | + |
| 57 | +This discrepancy is unfortunate as many users reach for `Self(v0, v1, ..)` |
| 58 | +from time to time, only to find that it doesn't work. This creates a break |
| 59 | +in the users intuition and becomes a papercut. It also has the effect that |
| 60 | +each user must remember this exception, making the rule-set to remember |
| 61 | +larger wherefore the language becomes more complex. |
| 62 | + |
| 63 | +There are good reasons why `Self { f0: v0, f1: v1, .. }` is allowed. |
| 64 | +Chiefly among those is that it becomes easier to refactor the code when |
| 65 | +one wants to rename type names. Another important reason is that only |
| 66 | +having to keep `Self` in mind means that a developer does not need to |
| 67 | +keep the type name fresh in their working memory. This is beneficial for |
| 68 | +users with shorter working memory such as the author of this RFC. |
| 69 | + |
| 70 | +Since `Self { f0: v0, .. }` is well motivated, those benefits and motivations |
| 71 | +will also extend to tuple and unit structs. Eliminating this discrepancy between |
| 72 | +tuple structs and those with named fields will therefore have all the benefits |
| 73 | +associated with this feature for structs with named fields. |
| 74 | + |
| 75 | +# Guide-level explanation |
| 76 | +[guide-level-explanation]: #guide-level-explanation |
| 77 | + |
| 78 | +## Basic concept |
| 79 | + |
| 80 | +For structs with named fields such as: |
| 81 | + |
| 82 | +```rust |
| 83 | +struct Person { |
| 84 | + name: String, |
| 85 | + ssn: usize, |
| 86 | + age: usize |
| 87 | +} |
| 88 | +``` |
| 89 | + |
| 90 | +You may use the syntax `Self { field0: value0, .. }` as seen below |
| 91 | +instead of writing `TypeName { field0: value0, .. }`: |
| 92 | + |
| 93 | +```rust |
| 94 | +impl Person { |
| 95 | + /// Make a newborn person. |
| 96 | + fn newborn(name: String, ssn: usize) -> Self { |
| 97 | + Self { name, ssn, age: 0 } |
| 98 | + } |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +## Through type aliases |
| 103 | + |
| 104 | +This ability does not extend to tuple structs however in current Rust but will |
| 105 | +with this RFC. To continue on with the previous example, you can now also write: |
| 106 | + |
| 107 | +```rust |
| 108 | +struct Person(String, usize, usize); |
| 109 | + |
| 110 | +impl Person { |
| 111 | + /// Make a newborn person. |
| 112 | + fn newborn(name: String, ssn: usize) -> Self { |
| 113 | + Self(name, ssn, 0) |
| 114 | + } |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +As with structs with named fields, you may also use `Self` when |
| 119 | +you are `impl`ing on a type alias of a struct as seen here: |
| 120 | + |
| 121 | +```rust |
| 122 | +struct FooBar(u8); |
| 123 | + |
| 124 | +type BarFoo = FooBar; |
| 125 | + |
| 126 | +impl Default for BarFoo { |
| 127 | + fn default() -> Self { |
| 128 | + Self(42) // <-- Not allowed before. |
| 129 | + } |
| 130 | +} |
| 131 | +``` |
| 132 | + |
| 133 | +## Patterns |
| 134 | + |
| 135 | +Currently, you can pattern match using `Self { .. }` on a named struct as in |
| 136 | +the following example: |
| 137 | + |
| 138 | +```rust |
| 139 | +struct Person { |
| 140 | + ssn: usize, |
| 141 | + age: usize |
| 142 | +} |
| 143 | + |
| 144 | +impl Person { |
| 145 | + /// Make a newborn person. |
| 146 | + fn newborn(ssn: usize) -> Self { |
| 147 | + match { Self { ssn, age: 0 } } { |
| 148 | + Self { ssn, age } // `Self { .. }` is permitted as a pattern! |
| 149 | + => Self { ssn, age } |
| 150 | + } |
| 151 | + } |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +This RFC extends this to tuple structs: |
| 156 | + |
| 157 | +```rust |
| 158 | +struct Person(usize, usize); |
| 159 | + |
| 160 | +impl Person { |
| 161 | + /// Make a newborn person. |
| 162 | + fn newborn(ssn: usize) -> Self { |
| 163 | + match { Self(ssn, 0) } { |
| 164 | + Self(ssn, age) // `Self(..)` is permitted as a pattern! |
| 165 | + => Self(ssn, age) |
| 166 | + } |
| 167 | + } |
| 168 | +} |
| 169 | +``` |
| 170 | + |
| 171 | +Of course, this redundant reconstruction is not recommended in actual code, |
| 172 | +but illustrates what you can do. |
| 173 | + |
| 174 | +## `Self` as a function pointer |
| 175 | + |
| 176 | +When you define a tuple struct today such as: |
| 177 | + |
| 178 | +```rust |
| 179 | +struct Foo<T>(T); |
| 180 | + |
| 181 | +impl<T> Foo<T> { |
| 182 | + fn fooify_iter(iter: impl Iterator<Item = T>) -> impl Iterator<Item = Foo<T>> { |
| 183 | + iter.map(Foo) |
| 184 | + } |
| 185 | +} |
| 186 | +``` |
| 187 | + |
| 188 | +you can use `Foo` as a function pointer typed at: `for<T> fn(T) -> T` as |
| 189 | +seen in the example above. |
| 190 | + |
| 191 | +This RFC extends that such that `Self` can also be used as a function pointer |
| 192 | +for tuple structs. Modifying the example above gives us: |
| 193 | + |
| 194 | +```rust |
| 195 | +impl<T> Foo<T> { |
| 196 | + fn fooify_iter(iter: impl Iterator<Item = T>) -> impl Iterator<Item = Foo<T>> { |
| 197 | + iter.map(Self) |
| 198 | + } |
| 199 | +} |
| 200 | +``` |
| 201 | + |
| 202 | +## Unit structs |
| 203 | + |
| 204 | +With this RFC, you can also use `Self` in pattern and expression contexts when |
| 205 | +dealing with unit structs. For example: |
| 206 | + |
| 207 | +```rust |
| 208 | +struct TheAnswer; |
| 209 | + |
| 210 | +impl Default for TheAnswer { |
| 211 | + fn default() -> Self { |
| 212 | + match { Self } { Self => Self } |
| 213 | + } |
| 214 | +} |
| 215 | +``` |
| 216 | + |
| 217 | +## Teaching the contents |
| 218 | + |
| 219 | +This RFC should not require additional effort other than spreading the |
| 220 | +news that this now is possible as well as the reference. The changes are |
| 221 | +seen as intuitive enough that it supports what the user already assumes |
| 222 | +should work and will probably try at some point. |
| 223 | + |
| 224 | +# Reference-level explanation |
| 225 | +[reference-level-explanation]: #reference-level-explanation |
| 226 | + |
| 227 | +When entering one of the following contexts, a Rust compiler will extend |
| 228 | +the value namespace with `Self` which maps to the tuple constructor `fn` |
| 229 | +in the case of tuple struct, or a constant, in the case of a unit struct: |
| 230 | + |
| 231 | ++ inherent `impl`s where the `Self` type is a tuple or unit struct |
| 232 | ++ `trait` `impl`s where the `Self` type is a tuple or unit struct |
| 233 | + |
| 234 | +As a result, when referring to a tuple struct, `Self` can be legally coerced |
| 235 | +into an `fn` pointer which accepts and returns expressions of the same type as |
| 236 | +the function pointer `Self` is referring to accepts. |
| 237 | + |
| 238 | +Another consequence is that `Self(p_0, .., p_n)` and `Self` become |
| 239 | +legal patterns. This works since `TupleCtor(p_0, .., p_n)` patterns are |
| 240 | +handled by resolving them in the value namespace and checking that they |
| 241 | +resolve to a tuple constructor. Since by definition, `Self` referring |
| 242 | +to a tuple struct resolves to a tuple constructor, this is OK. |
| 243 | + |
| 244 | +## Implementation notes |
| 245 | + |
| 246 | +As an additional check on the sanity of a Rust compiler implementation, |
| 247 | +a well formed expression `Self(v0, v1, ..)`, must be semantically equivalent to |
| 248 | +`Self { 0: v0, 1: v1, .. }` and must also be permitted when the latter would. |
| 249 | +Likewise the pattern `Self(p0, p1, ..)` must match exactly the same set of |
| 250 | +values as `Self { 0: p0, 1: p1, .. }` would and must be permitted when |
| 251 | +`Self { 0: p0, 1: p1, .. }` is well formed. |
| 252 | + |
| 253 | +Furthermore, a well formed expression or pattern `Self` must be semantically |
| 254 | +equivalent to `Self {}` and permitted when `Self {}` is well formed in the |
| 255 | +same context. |
| 256 | + |
| 257 | +For example for tuple structs, we have the typing rule: |
| 258 | + |
| 259 | +``` |
| 260 | +Δ ⊢ τ_0 type .. Δ ⊢ τ_n type |
| 261 | +Δ ⊢ Self type |
| 262 | +Γ ⊢ x_0 : τ_0 .. Γ ⊢ x_n : τ_n |
| 263 | +Γ ⊢ Self { 0: x_0, .. n: x_n } : Self |
| 264 | +----------------------------------------- |
| 265 | +Γ ⊢ Self ( x_0, .., x_n ) : Self |
| 266 | +``` |
| 267 | + |
| 268 | +and the operational semantics: |
| 269 | + |
| 270 | +``` |
| 271 | +Γ ⊢ Self { 0: e_0, .., n: e_n } ⇓ v |
| 272 | +------------------------------------- |
| 273 | +Γ ⊢ Self { e_0, .., e_n } ⇓ v |
| 274 | +``` |
| 275 | + |
| 276 | +for unit structs, the following holds: |
| 277 | + |
| 278 | +``` |
| 279 | +Δ ⊢ Self type |
| 280 | +Γ ⊢ Self {} : Self |
| 281 | +----------------------------------------- |
| 282 | +Γ ⊢ Self : Self |
| 283 | +``` |
| 284 | + |
| 285 | +with the operational semantics: |
| 286 | + |
| 287 | +``` |
| 288 | +Γ ⊢ Self {} ⇓ v |
| 289 | +------------------------------------- |
| 290 | +Γ ⊢ Self ⇓ v |
| 291 | +``` |
| 292 | + |
| 293 | +## In relation to other RFCs |
| 294 | + |
| 295 | +This RFC expands on [RFC 593] and [RFC 1647] with |
| 296 | +respect to where the keyword `Self` is allowed. |
| 297 | + |
| 298 | +[RFC 593]: 0593-forbid-Self-definitions.md |
| 299 | +[RFC 1647]: 1647-allow-self-in-where-clauses.md |
| 300 | + |
| 301 | +# Drawbacks |
| 302 | +[drawbacks]: #drawbacks |
| 303 | + |
| 304 | +There are potentially some, but the author could not think of any. |
| 305 | + |
| 306 | +# Rationale and alternatives |
| 307 | +[alternatives]: #alternatives |
| 308 | + |
| 309 | +This is the only design that makes sense in the sense that there really |
| 310 | +aren't any other. Potentially, `Self(v0, ..)` should only work when the |
| 311 | +`impl`ed type is not behind a type alias. However, since structs with named |
| 312 | +fields supports type aliases in this respect, so should tuple structs. |
| 313 | + |
| 314 | +Not providing this feature would preserve papercuts |
| 315 | +and unintuitive surprises for developers. |
| 316 | + |
| 317 | +# Unresolved questions |
| 318 | +[unresolved]: #unresolved-questions |
| 319 | + |
| 320 | +There are none. |
0 commit comments