Skip to content

Commit d391d85

Browse files
authored
Merge pull request #2302 from Centril/rfc/tuple-struct-self-ctor
RFC: Tuple struct construction with `Self(v1, v2, ..)`
2 parents 15e4dfd + 1951919 commit d391d85

File tree

1 file changed

+320
-0
lines changed

1 file changed

+320
-0
lines changed

text/2302-tuple-struct-self-ctor.md

+320
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
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

Comments
 (0)