| // run | |
| // Copyright 2021 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package main | |
| import ( | |
| "fmt" | |
| "strconv" | |
| ) | |
| // Various implementations of fromStrings(). | |
| type Setter[B any] interface { | |
| Set(string) | |
| *B | |
| } | |
| // Takes two type parameters where PT = *T | |
| func fromStrings1[T any, PT Setter[T]](s []string) []T { | |
| result := make([]T, len(s)) | |
| for i, v := range s { | |
| // The type of &result[i] is *T which is in the type list | |
| // of Setter, so we can convert it to PT. | |
| p := PT(&result[i]) | |
| // PT has a Set method. | |
| p.Set(v) | |
| } | |
| return result | |
| } | |
| func fromStrings1a[T any, PT Setter[T]](s []string) []PT { | |
| result := make([]PT, len(s)) | |
| for i, v := range s { | |
| // The type new(T) is *T which is in the type list | |
| // of Setter, so we can convert it to PT. | |
| result[i] = PT(new(T)) | |
| p := result[i] | |
| // PT has a Set method. | |
| p.Set(v) | |
| } | |
| return result | |
| } | |
| // Takes one type parameter and a set function | |
| func fromStrings2[T any](s []string, set func(*T, string)) []T { | |
| results := make([]T, len(s)) | |
| for i, v := range s { | |
| set(&results[i], v) | |
| } | |
| return results | |
| } | |
| type Setter2 interface { | |
| Set(string) | |
| } | |
| // Takes only one type parameter, but causes a panic (see below) | |
| func fromStrings3[T Setter2](s []string) []T { | |
| results := make([]T, len(s)) | |
| for i, v := range s { | |
| // Panics if T is a pointer type because receiver is T(nil). | |
| results[i].Set(v) | |
| } | |
| return results | |
| } | |
| // Two concrete types with the appropriate Set method. | |
| type SettableInt int | |
| func (p *SettableInt) Set(s string) { | |
| i, err := strconv.Atoi(s) | |
| if err != nil { | |
| panic(err) | |
| } | |
| *p = SettableInt(i) | |
| } | |
| type SettableString struct { | |
| s string | |
| } | |
| func (x *SettableString) Set(s string) { | |
| x.s = s | |
| } | |
| func main() { | |
| s := fromStrings1[SettableInt, *SettableInt]([]string{"1"}) | |
| if len(s) != 1 || s[0] != 1 { | |
| panic(fmt.Sprintf("got %v, want %v", s, []int{1})) | |
| } | |
| s2 := fromStrings1a[SettableInt, *SettableInt]([]string{"1"}) | |
| if len(s2) != 1 || *s2[0] != 1 { | |
| x := 1 | |
| panic(fmt.Sprintf("got %v, want %v", s2, []*int{&x})) | |
| } | |
| // Test out constraint type inference, which should determine that the second | |
| // type param is *SettableString. | |
| ps := fromStrings1[SettableString]([]string{"x", "y"}) | |
| if len(ps) != 2 || ps[0] != (SettableString{"x"}) || ps[1] != (SettableString{"y"}) { | |
| panic(s) | |
| } | |
| s = fromStrings2([]string{"1"}, func(p *SettableInt, s string) { p.Set(s) }) | |
| if len(s) != 1 || s[0] != 1 { | |
| panic(fmt.Sprintf("got %v, want %v", s, []int{1})) | |
| } | |
| defer func() { | |
| if recover() == nil { | |
| panic("did not panic as expected") | |
| } | |
| }() | |
| // This should type check but should panic at run time, | |
| // because it will make a slice of *SettableInt and then call | |
| // Set on a nil value. | |
| fromStrings3[*SettableInt]([]string{"1"}) | |
| } |