proposal: Go 2: require field name for struct composite literal when the struct reference is with a selector #27423
Labels
FrozenDueToAge
LanguageChange
Suggested changes to the Go language
Proposal
v2
An incompatible library change
Milestone
Background
Adding a new field to a struct in a package is currently a breaking change. This means that adding a field to any public struct in a package requires a new major package version, creating a fair amount of churn for users when coupled with modules.
One major reason this is a breaking change is because of the second rule of Composite literals that allows to not specify each individual key name, and use a list instead, i.e.
image.Point{10, 20}
.Adding methods to a struct is generally not considered a breaking change (even if it is) and it is frequently done in the Go standard library.
The net result is that it is very hard to write future proof structs, as adding a struct fields is a generally considered a breaking change due to the allowance of keyless composite literal rule for struct imported from an external package.
Precedent
go vet already warns on keyless struct composite literal.
Proposal
When a struct composite literal appears in the code, and the struct symbol reference uses a selector, that is, it is not in the current package, then field names are required.
This means changing the composite literal spec from:
For struct literals the following rules apply:
It is an error to specify an element for a non-exported field of a struct belonging to a different package.
to
For struct literals the following rules apply:
It is an error to specify an element for a non-exported field of a struct belonging to a different package.
This is effectively turning the go vet composites rule into a compile rule, removing the need for the go vet check.
Rationale
Enable package authors to add fields to struct the same way they feel safe to add methods to struct.
Automatic fix
While this is a breaking change in the language, as current code that use struct composite literal from imported packages with keyless list would stop compiling, it is a relatively easy fix that could be 100% automated.
Caveats
Breaking change due to embedding
Adding a field to a struct would still be a breaking change when the struct is embedded into another one, as described in this post. Still, the cases where this occurs are expected to be significantly lesser than struct composite literal, resulting in a net benefits.
Verbosity
The rationale to disallow non-keyed list only when a selector is used enables using non-keyed list in unit tests, which are often verbose.
The obvious downside is that other packages will be more verbose in the struct composite, but long term forward compatibility is worth the price.
Example
Synthetic
Source foo.go
In foo_test.go, it would be allowed to use the short form
T{"hi"}
because the typeT
is referred to without a selector.On the other hand, in source bar.go:
Using
foo.T{"hi"}
would be disallowed because typeT
is referred to via selectorfoo.
.Real world
I'd like to add a new field to the struct physic.Env as the BME680 sensor supports gas measurement, but I can't add a field to this struct without bumping periph's major version from 3 to 4. This is because I don't know which code will break.
The text was updated successfully, but these errors were encountered: