Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Field init shorthand #1682

Merged
merged 16 commits into from
Oct 22, 2016
Merged
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions text/0000-named-field-puns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
- Feature Name: named-field-puns
- Start Date: 2016-07-18
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

When initializing a data structure (struct, enum, union) with named fields,
allow writing `fieldname` as a shorthand for `fieldname: fieldname`. This
allows a compact syntax for initialization, with less duplication.

Example usage:

struct SomeStruct { field1: ComplexType, field2: AnotherType }

impl SomeStruct {
fn new() -> Self {
let field1 = {
// Various initialization code
};
let field2 = {
// More initialization code
};
SomeStruct { complexField, anotherField }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this line be SomeStruct { field1, field2 }?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sure should, thanks!

}
}

# Motivation
[motivation]: #motivation

When writing initialization code for a data structure, the names of the
structure fields often become the most straightforward names to use for their
initial values as well. At the end of such an initialization function, then,
the initializer will contain many patterns of repeated field names as field
values: `field: field, field2: field2, field3: field3`.

Such repetition of the field names makes it less ergonomic to separately
declare and initialize individual fields, and makes it tempting to instead
embed complex code directly in the initializer to avoid repetition.

Rust already allows
[similar syntax for destructuring in pattern matches](https://doc.rust-lang.org/book/patterns.html#destructuring):
a pattern match can use `SomeStruct { field1, field2 } => ...` to match
`field1` and `field2` into values with the same names. This RFC introduces
symmetrical syntax for initializers.

A family of related structures will often use the same field name for a
semantically-shared value. Combining this new syntax with the existing
pattern-matching syntax allows simple movement of data between fields with a
pattern match: `Struct1 { field1, .. } => Struct2 { field1 }`.

The proposed syntax also improves structure initializers in closures, such as
might appear in a chain of iterator adapters: `|field1, field2| SomeStruct {
field1, field2 }`.

This RFC takes inspiration from the Haskell
[NamedFieldPuns extension](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#record-puns),
and from ES6
[shorthand property names](http://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer).

# Detailed design
[design]: #detailed-design

In the initializer for a `struct` with named fields, a `union` with named
fields, or an enum variant with named fields, accept an identifier `field` as a
shorthand for `field: field`.

The shorthand initializer `field` always behaves in every possible way like the
longhand initializer `field: field`. This RFC introduces no new behavior or
semantics, only a purely syntactic shorthand. The rest of this section only
provides further examples to explicitly clarify that this new syntax remains
entirely orthogonal to other initializer behavior and semantics.

If the struct `SomeStruct` has fields `field1` and `field2`, the initializer
`SomeStruct { field1, field2 }` behaves in every way like the initializer
`SomeStruct { field1: field1, field2: field2 }`.

An initializer may contain any combination of shorthand and full field
initializers:

let a = SomeStruct { field1, field2: expression, field3 };
let b = SomeStruct { field1: field1, field2: expression, field3: field3 };
assert_eq!(a, b);

An initializer may use shorthand field initializers together with
[update syntax](https://doc.rust-lang.org/book/structs.html#update-syntax):

let a = SomeStruct { field1, .. someStructInstance };
let b = SomeStruct { field1: field1, .. someStructInstance };
assert_eq!(a, b);

This shorthand initializer syntax does not introduce any new compiler errors
that cannot also occur with the longhand initializer syntax `field: field`.
Existing compiler errors that can occur with the longhand initializer syntax
`field: field` also apply to the shorthand initializer syntax `field`:

- As with the longhand initializer `field: field`, if the structure has no
field with the specified name `field`, the shorthand initializer `field`
results in a compiler error for attempting to initialize a non-existent
field.

- As with the longhand initializer `field: field`, repeating a field name
within the same initializer results in a compiler error
([E0062](https://doc.rust-lang.org/error-index.html#E0062)); this occurs with
any combination of shorthand initializers or full `field: expression`
initializers.

- As with the longhand initializer `field: field`, if the name `field` does not
resolve, the shorthand initializer `field` results in a compiler error for an
unresolved name ([E0425](https://doc.rust-lang.org/error-index.html#E0425)).

- As with the longhand initializer `field: field`, if the name `field` resolves
to a value with type incompatible with the field `field` in the structure,
the shorthand initializer `field` results in a compiler error for mismatched
types ([E0308](https://doc.rust-lang.org/error-index.html#E0308)).

# Drawbacks
[drawbacks]: #drawbacks

This new syntax could significantly improve readability given clear and local
field-punning variables, but could also be abused to decrease readability if
used with more distant variables.

As with many syntactic changes, a macro could implement this instead. See the
Alternatives section for discussion of this.

The shorthand initializer syntax looks similar to positional initialization of
a structure without field names; reinforcing this, the initializer will
commonly list the fields in the same order that the struct declares them.
However, the shorthand initializer syntax differs from the positional
initializer syntax (such as for a tuple struct) in that the positional syntax
uses parentheses instead of braces: `SomeStruct(x, y)` is unambiguously a
positional initializer, while `SomeStruct { x, y }` is unambiguously a
shorthand initializer for the named fields `x` and `y`.

# Alternatives
[alternatives]: #alternatives

In addition to this syntax, initializers could support omitting the field names
entirely, with syntax like `SomeStruct { .. }`, which would implicitly
initialize omitted fields from identically named variables. However, that would
introduce far too much magic into initializers, and the context-dependence
seems likely to result in less readable, less obvious code.

A macro wrapped around the initializer could implement this syntax, without
changing the language; for instance, `pun! { SomeStruct { field1, field2 } }`
could expand to `SomeStruct { field1: field1, field2: field2 }`. However, this
change exists to make structure construction shorter and more expressive;
having to use a macro would negate some of the benefit of doing so,
particularly in places where brevity improves readability, such as in a closure
in the middle of a larger expression. There is also precedent for
language-level support. Pattern matching already allows using field names as
the _destination_ for the field values via destructuring. This change adds a
symmetrical mechanism for construction which uses existing names as _sources_.