Description
Updates:
only allowunpack
when all fields are exportedunpack
skips unexported fields of structs from different packages- include a vet rule
- explicitly include a change to Go 1 compat agreement so that
unpack
is treated the same as unkeyed struct literals
This proposal adds basic tuples to Go with one piece of sugar and two builtins.
The sugar is for struct(T0, T1, …, Tn)
to be shorthand for a struct type with n
fields where the i
th field has type Ti
and is named fmt.Sprintf("F%d", i)
. For example, struct(int, string)
is a more compact way to write struct { F0 int; F1 string }
.
This gives us a tuple type notation for and answers all questions about how they behave (as the desugared struct they are equivalent to). By naming the fields F0
and so on, this both provides accessors for the individual elements and states that all tuple fields are exported.
The variadic pack
builtin returns an anonymous tuple, with the appropriate types and values from its arguments. In particular, by ordinary function call rules, this allows conversion of a function with multiple returns into a tuple. So pack(1, "x")
is equivalent to struct(int, string){1, "x"}
and, given func f() (int, error)
, the statement t := pack(f())
produces the same value for t
as the below:
n, err := f()
t := struct(int, error){n, err}
The unpack
builtin takes any struct value and returns all of fields in the order of their definition, skipping _
fields and unexported fields from a different package. (This has to be somewhat more generally defined as tuples aren't a separate concept in the language under this proposal.) This is always the inverse of pack
. Example:
// in goroutine 1
c <- pack(cmd_repeat, n)
// in goroutine 2
cmd, payload := unpack(<-c)
The struct()
sugar let's us write pairs, triples, and so on for values of mixed types without having to worry about names. The pack
and unpack
builtins make it easier to produce and consume these values.
No changes are needed to the public API of reflect
or go/types
to handle tuples as they're just structs, though helpers to determine if a given struct is "tuple-y" may be useful. go/ast
would need a flag in StructType
noting when a struct used the tuple syntax but as long as the implicit field names are explicitly added by the parser. The only place this would be needed is for converting an AST back to a string.
The only potential danger here is unpack
. If it's used on a non-tuple struct type from a different package it would be a breaking change for that package to add an additional exported field. Go 1 compat should be updated to say that this is a acceptable just as it says that adding a field that breaks an unkeyed struct literal is acceptable. Additionally, a go vet check should be added that limits unpack
to structs with exclusively "F0", "F1", …, "Fn" field names. This can be relaxed at a later time.
This is a polished version of an earlier comment of mine: #33080 (comment) In the years since I've written many one-off types that could have just been tuples and experimented with generics+code generation to fill in the gap. There have been multiple calls for tuples in random threads here and there and a few proposals: