├── .gitignore ├── README.md ├── add └── main.go ├── anonymous-func ├── go.mod └── main.go ├── benchmark ├── README.md ├── bench_test.go └── go.mod ├── binary-size ├── Makefile ├── README.md ├── go.mod ├── main1.go └── main2.go ├── build.sh ├── byteseq ├── go.mod └── main.go ├── comparable └── main.go ├── constraints-chan ├── README.md ├── go.mod └── main.go ├── constraints-slice ├── README.md ├── go.mod └── main.go ├── constraints-vector ├── README.md ├── go.mod └── main.go ├── every └── main.go ├── exp_maps ├── go.mod ├── go.sum └── main.go ├── exp_slices ├── go.mod ├── go.sum └── main.go ├── filter └── main.go ├── foreach └── main.go ├── functional-options ├── go.mod └── main.go ├── generator └── main.go ├── go.mod ├── interface └── main.go ├── list ├── go.mod └── main.go ├── map └── main.go ├── minmax └── main.go ├── must ├── go.mod └── main.go ├── perm └── main.go ├── pointerof └── main.go ├── reduce └── main.go ├── reflect ├── go.mod └── main.go ├── shuffle └── main.go ├── some └── main.go ├── splittraintest └── main.go ├── ternaryop └── main.go ├── tuple └── main.go ├── underlying ├── go.mod └── main.go └── uniq └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-generics-example 2 | 3 | Example code for Go generics. 4 | 5 | ## Usage 6 | 7 | ``` 8 | $ go build -gcflags=-G=3 9 | ``` 10 | 11 | ## Requirements 12 | 13 | Go 1.18 or later 14 | 15 | ## Third-party packages that support Generics 16 | 17 | - [Code-Hex/go-generics-cache](https://github.com/Code-Hex/go-generics-cache) An in-memory key:value store/cache library written in Go 1.18 generics 18 | - [genkami/dogs](https://github.com/genkami/dogs) Dogs is a library that provides some useful types and functions borrowed from functional languages. 19 | - [makiuchi-d/tuple](https://github.com/makiuchi-d/tuple) Definition of n-tuple structs using type parameters. 20 | - [samber/lo](https://github.com/samber/lo) 💥 A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...) 21 | - [mattn/go-result](https://github.com/mattn/go-result) Something like unwrap in Rust. 22 | - [makiuchi-d/linq](https://github.com/makiuchi-d/linq) LINQ for Go with type parameters. 23 | 24 | ## Advertise 25 | 26 | [Go 言語にやってくる Generics は我々に何をもたらすのか](https://zenn.dev/mattn/books/4c7de85ec42cb44cf285) 27 | 28 | ## License 29 | 30 | MIT 31 | 32 | ## Author 33 | 34 | Yasuhiro Matsumoto (a.k.a. mattn) 35 | -------------------------------------------------------------------------------- /add/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Addable interface { 8 | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64 | complex64 | complex128 | string 9 | } 10 | 11 | func add[T Addable](a, b T) T { 12 | return a + b 13 | } 14 | 15 | func main() { 16 | fmt.Println(add(1, 2)) 17 | 18 | fmt.Println(add("foo", "bar")) 19 | } 20 | -------------------------------------------------------------------------------- /anonymous-func/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/anonymous-func 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /anonymous-func/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func makeComparableFunc[T comparable]() func(lhs, rhs T) bool { 6 | return func(lhs, rhs T) bool { 7 | return lhs == rhs 8 | } 9 | } 10 | 11 | func main() { 12 | // https://github.com/golang/go/issues/39632 13 | //equal := func[T comparable](lhs, rhs T) bool { 14 | // return lhs == rhs 15 | //} 16 | 17 | equal := makeComparableFunc[int]() 18 | fmt.Println(equal(1, 2)) 19 | } 20 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | # benchmark 2 | 3 | ``` 4 | goos: windows 5 | goarch: amd64 6 | pkg: github.com/mattn/go-generics-example/benchmark 7 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx 8 | BenchmarkWithoutGenerics-8 53993007 20.37 ns/op 9 | BenchmarkWithGenerics-8 69206546 20.98 ns/op 10 | BenchmarkWithInterface-8 54277022 21.20 ns/op 11 | PASS 12 | ok github.com/mattn/go-generics-example/benchmark 4.692s 13 | ``` 14 | -------------------------------------------------------------------------------- /benchmark/bench_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func addInt(a, b int) int { 8 | return a + b 9 | } 10 | 11 | func addString(a, b string) string { 12 | return a + b 13 | } 14 | 15 | func BenchmarkWithoutGenerics(b *testing.B) { 16 | for i := 0; i < b.N; i++ { 17 | _ = addInt(1, 2) 18 | _ = addString("foo", "bar") 19 | } 20 | } 21 | 22 | type Addable interface { 23 | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64 | complex64 | complex128 | string 24 | } 25 | 26 | func add[T Addable](a, b T) T { 27 | return a + b 28 | } 29 | 30 | func BenchmarkWithGenerics(b *testing.B) { 31 | for i := 0; i < b.N; i++ { 32 | _ = add(1, 2) 33 | _ = add("foo", "bar") 34 | } 35 | } 36 | 37 | func addInterface(a, b interface{}) interface{} { 38 | switch v := a.(type) { 39 | case int: 40 | return v + b.(int) 41 | case string: 42 | return v + b.(string) 43 | default: 44 | panic("unrecognized type") 45 | } 46 | } 47 | 48 | func BenchmarkWithInterface(b *testing.B) { 49 | for i := 0; i < b.N; i++ { 50 | _ = addInterface(1, 2).(int) 51 | _ = addInterface("foo", "bar").(string) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /benchmark/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/benchmark 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /binary-size/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .go .s 2 | 3 | all: main1 main2 main1.s main2.s 4 | 5 | main1: main1.go 6 | go build main1.go 7 | 8 | main2: main2.go 9 | go build main2.go 10 | 11 | .go.s: 12 | go tool compile -S $< > $@ 13 | 14 | clean: 15 | rm -f *.s *.o main1 main2 16 | -------------------------------------------------------------------------------- /binary-size/README.md: -------------------------------------------------------------------------------- 1 | # binary-size 2 | 3 | ## Binary sizes are same with `go build` 4 | 5 | ``` 6 | $ make 7 | go tool compile -S main1.go > main1.s 8 | go tool compile -S main2.go > main2.s 9 | 10 | $ go build -o main1.exe main1.go 11 | 12 | $ go build -o main2.exe main2.go 13 | 14 | $ ls -la main1.exe main2.exe 15 | -rwxr-xr-x 1 mattn mattn 1163264 Nov 28 00:43 main1.exe 16 | -rwxr-xr-x 1 mattn mattn 1163264 Nov 28 00:43 main2.exe 17 | ``` 18 | -------------------------------------------------------------------------------- /binary-size/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/size 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /binary-size/main1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Addable interface { 4 | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64 | complex64 | complex128 | string 5 | } 6 | 7 | //go:noinline 8 | func add[T Addable](a, b T) T { 9 | return a + b 10 | } 11 | 12 | func main() { 13 | println(add(1, 2)) 14 | println(add("foo", "bar")) 15 | } 16 | -------------------------------------------------------------------------------- /binary-size/main2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //go:noinline 4 | func addInt(a, b int) int { 5 | return a + b 6 | } 7 | 8 | //go:noinline 9 | func addString(a, b string) string { 10 | return a + b 11 | } 12 | 13 | func main() { 14 | println(addInt(1, 2)) 15 | println(addString("foo", "bar")) 16 | } 17 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find . -type d -maxdepth 1 | while read line; do 4 | (cd $line; go build) 5 | done 6 | -------------------------------------------------------------------------------- /byteseq/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/byteseq 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /byteseq/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func add[S ~string | ~[]byte](buf *[]byte, s S) { 8 | *buf = append(*buf, s...) 9 | } 10 | 11 | func main() { 12 | var buf []byte 13 | add(&buf, "foo") 14 | add(&buf, []byte("bar")) 15 | 16 | fmt.Println(string(buf)) 17 | } 18 | -------------------------------------------------------------------------------- /comparable/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // NOTE This won't work since operator == not defined for T 4 | // func findFunc[T any](a []T, v T) int { 5 | // for i, e := range a { 6 | // if e == v { 7 | // return i 8 | // } 9 | // } 10 | // return -1 11 | // } 12 | 13 | func findFunc[T comparable](a []T, v T) int { 14 | for i, e := range a { 15 | if e == v { 16 | return i 17 | } 18 | } 19 | return -1 20 | } 21 | 22 | func main() { 23 | print(findFunc([]int{1, 2, 3, 4, 5, 6}, 5)) 24 | } 25 | -------------------------------------------------------------------------------- /constraints-chan/README.md: -------------------------------------------------------------------------------- 1 | # constraints-chan 2 | 3 | This example no longer works 4 | 5 | https://github.com/golang/go/commit/79ff663754f4238bd1fe2e56f460c2f603c71b80 6 | 7 | https://github.com/golang/go/commit/070951c5dcc47c9cff2ad4c1ac6170a4060a4d0c 8 | 9 | -------------------------------------------------------------------------------- /constraints-chan/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/constraints-chan 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /constraints-chan/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | func makeChan[T chan E, E ~int](ctx context.Context, arr []E) T { 9 | ch := make(T) 10 | go func() { 11 | defer close(ch) 12 | for _, v := range arr { 13 | select { 14 | case <-ctx.Done(): 15 | return 16 | default: 17 | } 18 | ch <- v 19 | } 20 | }() 21 | return ch 22 | } 23 | 24 | func main() { 25 | for v := range makeChan(context.Background(), []int{1, 2, 3}) { 26 | fmt.Println(v) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /constraints-slice/README.md: -------------------------------------------------------------------------------- 1 | # constraints-slice 2 | 3 | This example no longer works 4 | 5 | https://github.com/golang/go/commit/79ff663754f4238bd1fe2e56f460c2f603c71b80 6 | 7 | https://github.com/golang/go/commit/070951c5dcc47c9cff2ad4c1ac6170a4060a4d0c 8 | 9 | -------------------------------------------------------------------------------- /constraints-slice/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/constraints-slice 2 | 3 | go 1.18 4 | 5 | require golang.org/x/exp v0.0.0-20220303002715-f922e1b6e9ab 6 | -------------------------------------------------------------------------------- /constraints-slice/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/exp/constraints" 7 | ) 8 | 9 | func sort[T []E, E constraints.Ordered](arr T) T { 10 | for i := 0; i < len(arr)-1; i++ { 11 | for j := 0; j < len(arr)-i-1; j++ { 12 | if arr[j] > arr[j+1] { 13 | arr[j], arr[j+1] = arr[j+1], arr[j] 14 | } 15 | } 16 | } 17 | return arr 18 | } 19 | 20 | func main() { 21 | fmt.Println(sort([]int{3, 1, 2})) 22 | fmt.Println(sort([]float64{6.2, 7.91, 2.1})) 23 | } 24 | -------------------------------------------------------------------------------- /constraints-vector/README.md: -------------------------------------------------------------------------------- 1 | # constraints-vector 2 | 3 | This example no longer works 4 | 5 | https://github.com/golang/go/commit/79ff663754f4238bd1fe2e56f460c2f603c71b80 6 | 7 | https://github.com/golang/go/commit/070951c5dcc47c9cff2ad4c1ac6170a4060a4d0c 8 | 9 | -------------------------------------------------------------------------------- /constraints-vector/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/constraints-vector 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /constraints-vector/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "constraints" 5 | "fmt" 6 | ) 7 | 8 | // type v[T constraints.Ordered] T 9 | 10 | type Vector[T constraints.Ordered] struct { 11 | x, y T 12 | } 13 | 14 | func (v *Vector[T]) Add(x, y T) { 15 | v.x += T(x) 16 | v.y += T(y) 17 | } 18 | 19 | func (v *Vector[T]) String() string { 20 | return fmt.Sprintf("{x: %v, y: %v}", v.x, v.y) 21 | } 22 | 23 | func NewVector[T constraints.Ordered](x, y T) *Vector[T] { 24 | return &Vector[T]{x: x, y: y} 25 | } 26 | 27 | func main() { 28 | v := NewVector[float64](1, 2) 29 | v.Add(2, 3) 30 | fmt.Println(v) 31 | } 32 | -------------------------------------------------------------------------------- /every/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func every[T any](a []T, f func(T) bool) bool { 8 | for _, e := range a { 9 | if !f(e) { 10 | return false 11 | } 12 | } 13 | 14 | return true 15 | } 16 | 17 | func main() { 18 | allEven := every([]int{2, 4, 6, 8, 10}, func(v int) bool { 19 | return v%2 == 0 20 | }) 21 | if allEven { 22 | fmt.Println("All even!") 23 | } 24 | 25 | allEven = every([]int{2, 3, 4, 5, 6}, func(v int) bool { 26 | return v%2 == 0 27 | }) 28 | if !allEven { 29 | fmt.Println("Some odd") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /exp_maps/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/exp_maps 2 | 3 | go 1.18 4 | 5 | require golang.org/x/exp v0.0.0-20211123021643-48cbe7f80d7c // indirect 6 | -------------------------------------------------------------------------------- /exp_maps/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/exp v0.0.0-20211123021643-48cbe7f80d7c h1:hp+QRBz/P/780ndA1Rv/UpvsR6AsVmOMGYitxgZ1PPA= 2 | golang.org/x/exp v0.0.0-20211123021643-48cbe7f80d7c/go.mod h1:b9TAUYHmRtqA6klRHApnXMnj+OyLce4yF5cZCUbk2ps= 3 | -------------------------------------------------------------------------------- /exp_maps/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/exp/maps" 7 | ) 8 | 9 | func main() { 10 | m := map[int]string{ 11 | 123: "foo", 12 | 456: "bar", 13 | } 14 | fmt.Println(maps.Keys(m)) // [123 456] 15 | fmt.Println(maps.Values(m)) // [foo bar] 16 | m2 := maps.Clone(m) 17 | fmt.Println(maps.Equal(m2, m)) // true 18 | m3 := map[int]string{ 19 | 789: "baz", 20 | } 21 | maps.Copy(m3, m) 22 | fmt.Println(maps.Equal(m3, m)) // false 23 | } 24 | -------------------------------------------------------------------------------- /exp_slices/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/exp_slices 2 | 3 | go 1.18 4 | 5 | require golang.org/x/exp v0.0.0-20211123021643-48cbe7f80d7c 6 | -------------------------------------------------------------------------------- /exp_slices/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/exp v0.0.0-20211123021643-48cbe7f80d7c h1:hp+QRBz/P/780ndA1Rv/UpvsR6AsVmOMGYitxgZ1PPA= 2 | golang.org/x/exp v0.0.0-20211123021643-48cbe7f80d7c/go.mod h1:b9TAUYHmRtqA6klRHApnXMnj+OyLce4yF5cZCUbk2ps= 3 | -------------------------------------------------------------------------------- /exp_slices/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/exp/slices" 7 | ) 8 | 9 | func main() { 10 | a := []string{ 11 | "January", 12 | "February", 13 | "March", 14 | "April", 15 | "May", 16 | "June", 17 | "July", 18 | "August", 19 | "September", 20 | "October", 21 | "November", 22 | "December", 23 | "END", 24 | } 25 | a = a[:12] 26 | fmt.Println(cap(a)) // 13 27 | a = slices.Clip(a) 28 | fmt.Println(cap(a)) // 12 29 | 30 | a = []string{ 31 | "foo", 32 | "foo", 33 | "bar", 34 | "baz", 35 | "foo", 36 | "baz", 37 | } 38 | a = slices.Compact(a) 39 | fmt.Println(a) // [foo bar baz foo baz] 40 | 41 | fmt.Println(slices.Contains(a, "zoo")) // false 42 | 43 | a = slices.Delete(a, 1, 3) 44 | fmt.Println(a) // [foo foo baz] 45 | 46 | fmt.Println(slices.Index(a, "baz")) // 2 47 | 48 | fmt.Println(cap(a)) // 6 49 | a = slices.Grow(a, 5) 50 | fmt.Println(cap(a)) // 12 WTF? 51 | 52 | a = slices.Insert(a, 0, "noo") 53 | fmt.Println(a) // [noo foo foo baz] 54 | } 55 | -------------------------------------------------------------------------------- /filter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func filterFunc[T any](a []T, f func(T) bool) []T { 8 | var n []T 9 | for _, e := range a { 10 | if f(e) { 11 | n = append(n, e) 12 | } 13 | } 14 | return n 15 | } 16 | 17 | func main() { 18 | vi := []int{1, 2, 3, 4, 5, 6} 19 | vi = filterFunc(vi, func(v int) bool { 20 | return v < 4 21 | }) 22 | fmt.Println(vi) 23 | } 24 | -------------------------------------------------------------------------------- /foreach/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func eachFunc[T any](a []T, f func(T) bool) { 8 | for _, e := range a { 9 | if !f(e) { 10 | break 11 | } 12 | } 13 | } 14 | 15 | func main() { 16 | eachFunc([]int{1, 2, 3, 4, 5, 6}, func(v int) bool { 17 | fmt.Println(v) 18 | return v < 4 19 | }) 20 | eachFunc([]string{"foo", "bar", "", "zoo"}, func(v string) bool { 21 | fmt.Println(v) 22 | return v != "" 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /functional-options/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/functional-options 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /functional-options/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Addable interface { 6 | ~int | ~int32 | ~int64 | ~float32 | ~float64 7 | } 8 | 9 | type Foo[T Addable] struct{ v T } 10 | 11 | func (foo *Foo[T]) Add(v T) { 12 | foo.v += v 13 | } 14 | 15 | type Option[T Addable] func(*Foo[T]) 16 | 17 | func WithValue[T Addable](v T) Option[T] { 18 | return func(f *Foo[T]) { f.v = v } 19 | } 20 | 21 | func NewFoo[T Addable](options ...Option[T]) *Foo[T] { 22 | foo := new(Foo[T]) 23 | for _, opt := range options { 24 | opt(foo) 25 | } 26 | return foo 27 | } 28 | 29 | func main() { 30 | foo := NewFoo(WithValue(3.14)) 31 | foo.Add(4) 32 | // 7.140000000000001 (float64) 33 | fmt.Printf("%v (%T)\n", foo.v, foo.v) 34 | } 35 | -------------------------------------------------------------------------------- /generator/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Addable interface { 8 | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64 | complex64 | complex128 9 | } 10 | 11 | func generator[T Addable](a T, v T) func() T { 12 | return func() T { 13 | r := a 14 | a = a + v 15 | return r 16 | } 17 | } 18 | 19 | func main() { 20 | f := generator(0, 1) 21 | fmt.Println(f()) 22 | fmt.Println(f()) 23 | fmt.Println(f()) 24 | } 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /interface/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Int64 interface { 4 | ~int64 | ~uint64 5 | } 6 | 7 | type NumberPlayer[T Int64] interface { 8 | AddCounter() T 9 | MinusCounter() T 10 | } 11 | 12 | type Counter[T Int64] struct { 13 | t T 14 | counter T 15 | } 16 | 17 | func (c *Counter[T]) AddCounter() T { 18 | c.t += c.counter 19 | return c.t 20 | } 21 | 22 | func (c *Counter[T]) MinusCounter() T { 23 | c.t -= c.counter 24 | return c.t 25 | } 26 | 27 | func NewNumberPlayer[T Int64](t T, counter T) NumberPlayer[T] { 28 | return &Counter[T]{ 29 | t: t, 30 | counter: counter, 31 | } 32 | } 33 | 34 | func main() { 35 | numbers := NewNumberPlayer(int64(32), int64(2)) 36 | println(numbers.AddCounter()) 37 | println(numbers.MinusCounter()) 38 | 39 | numbersAgain := NewNumberPlayer(uint64(32), uint64(9)) 40 | println(numbersAgain.AddCounter()) 41 | println(numbersAgain.MinusCounter()) 42 | } 43 | -------------------------------------------------------------------------------- /list/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/list 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /list/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type List[T comparable] struct { 8 | l []T 9 | } 10 | 11 | func (l *List[T]) Push(v T) { 12 | l.l = append(l.l, v) 13 | } 14 | 15 | func (l *List[T]) Insert(v T) { 16 | l.InsertAt(0, v) 17 | return 18 | } 19 | 20 | func (l *List[T]) InsertAt(pos int, v T) { 21 | l.l = append(l.l[:pos+1], l.l[pos:]...) 22 | l.l[pos] = v 23 | return 24 | } 25 | 26 | func (l *List[T]) Remove(i int) { 27 | l.l = l.l[:i+copy(l.l[i:], l.l[i+1:])] 28 | return 29 | } 30 | 31 | func (l *List[T]) Equals(rhs *List[T]) bool { 32 | if len(l.l) != len(rhs.l) { 33 | return false 34 | } 35 | for i := 0; i < len(l.l); i++ { 36 | if l.l[i] != rhs.l[i] { 37 | return false 38 | } 39 | } 40 | return true 41 | } 42 | 43 | func (l *List[T]) Clone() *List[T] { 44 | ll := &List[T]{l: make([]T, len(l.l))} 45 | copy(ll.l, l.l) 46 | return ll 47 | } 48 | 49 | func (l *List[T]) Slice() []T { 50 | return l.l 51 | } 52 | 53 | func NewList[T comparable](t []T) *List[T] { 54 | return &List[T]{l: t} 55 | } 56 | 57 | func main() { 58 | l := NewList([]int{1, 2, 3}) 59 | l.Push(4) 60 | l.Push(5) 61 | l.Push(6) 62 | fmt.Println(l.Slice()) 63 | l.Remove(3) 64 | fmt.Println(l.Slice()) 65 | c := l.Clone() 66 | fmt.Println(c.Equals(l)) 67 | } 68 | -------------------------------------------------------------------------------- /map/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func mapFunc[T any, M any](a []T, f func(T) M) []M { 8 | n := make([]M, len(a), cap(a)) 9 | for i, e := range a { 10 | n[i] = f(e) 11 | } 12 | return n 13 | } 14 | 15 | func main() { 16 | vi := []int{1, 2, 3, 4, 5, 6} 17 | vs := mapFunc(vi, func(v int) string { 18 | return "<" + fmt.Sprint(v) + ">" 19 | }) 20 | fmt.Println(vs) 21 | } 22 | -------------------------------------------------------------------------------- /minmax/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type comparable interface { 8 | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64 9 | } 10 | 11 | func max[T comparable](a []T) T { 12 | m := a[0] 13 | for _, v := range a { 14 | if m < v { 15 | m = v 16 | } 17 | } 18 | return m 19 | } 20 | 21 | func min[T comparable](a []T) T { 22 | m := a[0] 23 | for _, v := range a { 24 | if m > v { 25 | m = v 26 | } 27 | } 28 | return m 29 | } 30 | 31 | func main() { 32 | vi := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 33 | result := max(vi) 34 | fmt.Println(result) 35 | 36 | vi = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 37 | result = min(vi) 38 | fmt.Println(result) 39 | } 40 | -------------------------------------------------------------------------------- /must/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/must 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /must/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func Must[T any](v T, err error) T { 8 | if err != nil { 9 | panic(err.Error()) 10 | } 11 | return v 12 | } 13 | 14 | func Must2[T1 any, T2 any](v1 T1, v2 T2, err error) (T1, T2) { 15 | if err != nil { 16 | panic(err.Error()) 17 | } 18 | return v1, v2 19 | } 20 | 21 | func Must3[T1 any, T2 any, T3 any](v1 T1, v2 T2, v3 T3, err error) (T1, T2, T3) { 22 | if err != nil { 23 | panic(err.Error()) 24 | } 25 | return v1, v2, v3 26 | } 27 | 28 | func main() { 29 | f := Must(os.Create("test.txt")) 30 | defer os.Remove("test.txt") 31 | defer f.Close() 32 | f.Write([]byte("hello")) 33 | } 34 | -------------------------------------------------------------------------------- /perm/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func permInternal[T any](a []T, f func([]T), i int) { 8 | if i > len(a) { 9 | f(a) 10 | return 11 | } 12 | permInternal(a, f, i+1) 13 | for j := i + 1; j < len(a); j++ { 14 | a[i], a[j] = a[j], a[i] 15 | permInternal(a, f, i+1) 16 | a[i], a[j] = a[j], a[i] 17 | } 18 | } 19 | 20 | func perm[T any](a []T, f func([]T)) { 21 | permInternal(a, f, 0) 22 | 23 | } 24 | 25 | func main() { 26 | vi := []int{1, 2, 3} 27 | perm(vi, func(a []int) { 28 | fmt.Println(a) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /pointerof/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func pointerOf[T any](v T) *T { 8 | return &v 9 | } 10 | 11 | func main() { 12 | sp := pointerOf("foo") 13 | fmt.Println(*sp) 14 | 15 | ip := pointerOf(123) 16 | fmt.Println(*ip) 17 | *ip = 234 18 | fmt.Println(*ip) 19 | } 20 | -------------------------------------------------------------------------------- /reduce/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func reduceFunc[T any](a []T, f func(T, T) T, initial T) T { 8 | if len(a) == 0 || f == nil { 9 | var vv T 10 | return vv 11 | } 12 | 13 | l := len(a) - 1 14 | 15 | reduce := func(a []T, ff func(T, T) T, memo T, startPoint, direction, length int) T { 16 | result := memo 17 | index := startPoint 18 | 19 | for i := 0; i <= length; i++ { 20 | result = ff(result, a[index]) 21 | index += direction 22 | } 23 | 24 | return result 25 | } 26 | 27 | return reduce(a, f, initial, 0, 1, l) 28 | } 29 | 30 | func main() { 31 | vi := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 32 | result := reduceFunc(vi, func(lhs, rhs int) int { 33 | return lhs + rhs 34 | }, 1) 35 | fmt.Println(result) 36 | 37 | vs := []string{"x", "y", "z"} 38 | s := reduceFunc(vs, func(lhs, rhs string) string { 39 | return lhs + rhs 40 | }, "a") 41 | fmt.Println(s) 42 | } 43 | -------------------------------------------------------------------------------- /reflect/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/reflect 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /reflect/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type Addable interface { 9 | int | int64 10 | } 11 | 12 | func AddOne[T Addable](t T) T { 13 | return t + 1 14 | } 15 | 16 | func main() { 17 | // reflect know only instantiated-types. 18 | vf := reflect.ValueOf(AddOne[int64]) 19 | fmt.Println(vf.Type()) // func(int64) int64 20 | fmt.Println(vf.Type().In(0)) // int64 21 | } 22 | -------------------------------------------------------------------------------- /shuffle/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func shuffle[T any](a []T) { 10 | n := len(a) 11 | for i := n - 1; i >= 0; i-- { 12 | j := rand.Intn(i + 1) 13 | a[i], a[j] = a[j], a[i] 14 | } 15 | } 16 | 17 | func main() { 18 | rand.Seed(time.Now().UnixNano()) 19 | 20 | vi := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 21 | shuffle(vi) 22 | fmt.Println(vi) 23 | } 24 | -------------------------------------------------------------------------------- /some/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func some[T any](a []T, f func(T) bool) bool { 8 | for _, e := range a { 9 | if f(e) { 10 | return true 11 | } 12 | } 13 | 14 | return false 15 | } 16 | 17 | func main() { 18 | someEven := some([]int{2, 3, 4, 5, 6}, func(v int) bool { 19 | return v%2 == 0 20 | }) 21 | if someEven { 22 | fmt.Println("Some odd") 23 | } 24 | 25 | someEven = some([]int{1, 3, 5, 7}, func(v int) bool { 26 | return v%2 == 0 27 | }) 28 | if !someEven { 29 | fmt.Println("Only odd") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /splittraintest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func shuffle[T any](a []T) { 10 | n := len(a) 11 | for i := n - 1; i >= 0; i-- { 12 | j := rand.Intn(i + 1) 13 | a[i], a[j] = a[j], a[i] 14 | } 15 | } 16 | 17 | func splitTrainTest[T any](a []T, f float64) ([]T, []T) { 18 | aa := make([]T, len(a), len(a)) 19 | copy(aa, a) 20 | shuffle(aa) 21 | i := int(float64(len(aa)) * f) 22 | return aa[:i], aa[i:] 23 | } 24 | 25 | func main() { 26 | rand.Seed(time.Now().UnixNano()) 27 | 28 | vi := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 29 | train, test := splitTrainTest(vi, 0.75) 30 | fmt.Println(train, test) 31 | } 32 | -------------------------------------------------------------------------------- /ternaryop/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func ternaryOp[T any](s bool, t func() T, f func() T) T { 8 | if s { 9 | return t() 10 | } 11 | return f() 12 | } 13 | 14 | func main() { 15 | fmt.Println(ternaryOp(4 < 5, func() string { 16 | fmt.Println("left evaluated") 17 | return "less" 18 | }, func() string { 19 | fmt.Println("right evaluated") 20 | return "greater" 21 | })) 22 | } 23 | -------------------------------------------------------------------------------- /tuple/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | ) 7 | 8 | type tuple2[T1, T2 any] struct { 9 | V1 T1 10 | V2 T2 11 | } 12 | 13 | func zip2[T1, T2 any](v1 []T1, v2 []T2) []tuple2[T1, T2] { 14 | if len(v1) != len(v2) { 15 | panic("length of v1 and v2 must be same") 16 | } 17 | vv := make([]tuple2[T1, T2], len(v1), cap(v1)) 18 | for i := range v1 { 19 | vv[i].V1 = v1[i] 20 | vv[i].V2 = v2[i] 21 | } 22 | return vv 23 | } 24 | 25 | func unzip2[T1, T2 any](vv []tuple2[T1, T2]) ([]T1, []T2) { 26 | v1 := make([]T1, len(vv)) 27 | v2 := make([]T2, len(vv)) 28 | for i, v := range vv { 29 | v1[i] = v.V1 30 | v2[i] = v.V2 31 | } 32 | return v1, v2 33 | } 34 | 35 | func shuffle[T any](a []T) []T { 36 | rand.Shuffle(len(a), func(i, j int) { 37 | a[i], a[j] = a[j], a[i] 38 | }) 39 | return a 40 | } 41 | 42 | func main() { 43 | a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 44 | b := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"} 45 | a, b = unzip2(shuffle(zip2(a, b))) 46 | fmt.Println(a) 47 | fmt.Println(b) 48 | } 49 | -------------------------------------------------------------------------------- /underlying/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-generics-example/underlying 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /underlying/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Int64OrFloat64 interface { 4 | int64 | float64 5 | } 6 | 7 | type Int64LikeOrFloat64Like interface { 8 | ~int64 | ~float64 9 | } 10 | 11 | func V1[T Int64OrFloat64](v T) { println(v) } 12 | func V2[T Int64LikeOrFloat64Like](v T) { println(v) } 13 | 14 | func main() { 15 | // V1(1) // NG since 1 is not int64 16 | V1(int64(1)) // OK since it is int64 17 | type Int64 int64 18 | var v Int64 = 1 19 | // V1(v) // int64 but error since underlying type 20 | V2(v) // OK since V2 accept underlying type 21 | } 22 | -------------------------------------------------------------------------------- /uniq/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func uniq[T comparable](a []T) []T { 10 | u := make([]T, 0, len(a)) 11 | m := make(map[T]bool) 12 | for _, v := range a { 13 | if _, ok := m[v]; !ok { 14 | m[v] = true 15 | u = append(u, v) 16 | } 17 | } 18 | return u 19 | } 20 | 21 | func main() { 22 | rand.Seed(time.Now().UnixNano()) 23 | 24 | fmt.Println(uniq([]int{4, 3, 2, 3, 1, 5, 1})) 25 | } 26 | --------------------------------------------------------------------------------