diff options
| author | Ian Lance Taylor <iant@golang.org> | 2020-11-23 18:21:06 -0800 |
|---|---|---|
| committer | Ian Lance Taylor <iant@golang.org> | 2020-11-25 16:09:57 +0000 |
| commit | 303b7a5b4b2853bc0dacfde9aaceb7d5f14ca59b (patch) | |
| tree | 9d47f0f3c8eeb513bc681f9ae234540d41e95497 | |
| parent | 217dd4e7ebc3becaefdb5043b557bb01cdb6dd12 (diff) | |
| download | go-x-proposal-303b7a5b4b2853bc0dacfde9aaceb7d5f14ca59b.tar.xz | |
design: type parameter: add rule for composite types in type lists
Also clarify rules for type assertions to generic types and generic
types as cases in a type switch.
Change-Id: I27bc3c843ba5a57acb7c583c854ff8acd2c043d3
Reviewed-on: https://go-review.googlesource.com/c/proposal/+/272706
Trust: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
| -rw-r--r-- | design/go2draft-type-parameters.md | 143 |
1 files changed, 111 insertions, 32 deletions
diff --git a/design/go2draft-type-parameters.md b/design/go2draft-type-parameters.md index 3b2b71f..297b6c2 100644 --- a/design/go2draft-type-parameters.md +++ b/design/go2draft-type-parameters.md @@ -2,7 +2,7 @@ Ian Lance Taylor\ Robert Griesemer\ -September 21, 2020 +November 25, 2020 ## Abstract @@ -1846,6 +1846,69 @@ func Join[T byteseq](a []T, sep T) (ret T) { } ``` +For composite types (string, pointer, array, slice, struct, function, +map, channel) we impose an additional restriction: an operation may +only be used if the operator accepts identical input types (if any) +and produces identical result types for all of the types listed in the +type list. +To be clear, this additional restriction is only imposed when a +composite type appears in a type list. +It does not apply when a composite type is formed from a type +parameter outside of a type list, as in `var v []T` for some type +parameter `T`. + +``` +// structField is a type constraint with a list of structs that all +// have a field named x. +type structField interface { + type struct { a int; x int }, + struct { b int; x float64 }, + struct { c int; x uint64 } +} + +// This function is INVALID. +func IncrementX[T structField](p *T) { + v := p.x // INVALID: type of p.x is not the same for all types in list + v++ + p.x = v +} + +// sliceOrMap is a type constraint for a slice or a map. +type sliceOrMap interface { + type []int, map[int]int +} + +// Entry returns the i'th entry in a slice or the value of a map +// at key i. This is valid as the result of the operator is always int. +func Entry[T sliceOrMap](c T, i int) int { + // This is either a slice index operation or a map key lookup. + // Either way, the index and result types are type int. + return c[i] +} + +// sliceOrFloatMap is a type constraint for a slice or a map. +type sliceOrFloatMap interface { + type []int, map[float64]int +} + +// This function is INVALID. +// In this example the input type of the index operation is either +// int (for a slice) or float64 (for a map), so the operation is +// not permitted. +func FloatEntry[T sliceOrFloatMap](c T) int { + return c[1.0] // INVALID: input type is either int or float64. +} +``` + +Imposing this restriction makes it easier to reason about the type of +some operation in a generic function. +It avoids introducing the notion of a value whose type is the union of +a set of types found by applying some operation to each element of a +type list. + +(Note: with more understanding of how people want to write code, it +may be possible to relax this restriction in the future.) + #### Type parameters in type lists A type literal in a constraint can refer to type parameters of the @@ -1954,37 +2017,6 @@ func Add1024[T integer](s []T) { } ``` -#### Notes on composite types in type lists - -Type lists may include composite types. - -``` -type structField interface { - type struct { a int; x int }, - struct { b int; x float64 }, - struct { c int; x uint64 } -} - -func IncrementX[T structField](p *T) { - v := p.x - v++ - p.x = v -} -``` - -This constraint on the type parameter of `IncrementX` is such that -every valid type argument is a struct with a field `x` of some numeric -type. -Therefore, `IncrementX` is a valid function. -The type of `v` is a type based on a type parameter, with an implicit -constraint of `interface { type int, float64, uint64 }`. - -Admittedly this can get fairly complex, and there may be details here -that we don't fully understand. -In general, though, an operation (other than a method call) is -permitted if it would be permitted for each separate type in the type -list. - #### Type lists in embedded constraints When a constraint embeds another constraint, the type list of the @@ -3831,6 +3863,53 @@ func F(v S) int { } ``` +### Generic types as type switch cases + +A generic type may be used as the type in a type assertion or as a +case in a type switch. + +Here are some trivial examples: + +``` +func Assertion[T any](v interface{}) (T, bool) { + t, ok := v.(T) + return t, ok +} + +func Switch[T any](v interface{}) (T, bool) { + switch v := v.(type) { + case T: + return v, true + default: + var zero T + return zero, false + } +} +``` + +In a type switch, it's OK if a generic type turns out to duplicate +some other case in the type switch. +The first matching case is chosen. + +``` +func Switch2[T any](v interface{}) int { + switch v.(type) { + case T: + return 0 + case string: + return 1 + default: + return 2 + } +} + +// S2a will be set to 0. +var S2a = Switch2[string]("a string") + +// S2b will be set to 1. +var S2b = Switch2[int]("another string") +``` + ### Type inference for composite literals This is a feature we are not suggesting now, but could consider for |
