Description
Composite literals construct values for structs, arrays, slices, and maps. They consist of a type followed by a brace-bound list of elements. e.g.,
x := []string{"a", "b", "c"}
I propose adding untyped composite literals, which omit the type. Untyped composite literals are assignable to any composite type. They do not have a default type, and it is an error to use one as the right-hand-side of an assignment where the left-hand-side does not have an explicit type specified.
var x []string = {"a", "b", "c"}
var m map[string]int = {"a": 1}
type T struct {
V int
}
var s []*T = {{0}, {1}, {2}}
a := {1, 2, 3} // error: left-hand-type has no type specified
Go already allows the elision of the type of a composite literal under certain circumstances. This proposal extends that permission to all occasions when the literal type can be derived.
This proposal allows more concise code. Succinctness is a double-edged sword; it may increase or decrease clarity. I believe that the benefits in well-written code outweigh the harm in poorly-written code. We cannot prevent poor programmers from producing unclear code, and should not hamper good programmers in an attempt to do so.
This proposal may slightly simplify the language by removing the rules on when composite literal types may be elided.
Examples
Functions with large parameter lists are frequently written to take a single struct parameter instead. Untyped composite literals allow this pattern without introducing a single-purpose type or repetition.
// Without untyped composite literals...
type FooArgs struct {
A, B, C int
}
func Foo(args FooArgs) { ... }
Foo(FooArgs{A: 1, B: 2, C:3})
// ...or with.
func Foo(args struct {
A, B, C int
}) { ... }
Foo({A: 1, B: 2, C: 3})
In general, untyped composite literals can serve as lightweight tuples in a variety of situations:
ch := make(chan struct{
value string
err error
})
ch <- {value: "result"}
They also simplify code that returns a zero-valued struct and an error:
return time.Time{}, err
return {}, err // untyped composite literal
Code working with protocol buffers frequently constructs large, deeply nested composite literal values. These values frequently have types with long names dictated by the protobuf compiler. Eliding types will make code of this nature easier to write (and, arguably, read).
p.Options = append(p.Options, &foopb.Foo_FrotzOptions_Option{...}
p.Options = append(p.Options, {...}) // untyped composite literal