| // Copyright 2019 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. |
| |
| // This file shows some examples of type-parameterized functions. |
| |
| package p |
| |
| // Reverse is a generic function that takes a []T argument and |
| // reverses that slice in place. |
| func Reverse[T any](list []T) { |
| i := 0 |
| j := len(list)-1 |
| for i < j { |
| list[i], list[j] = list[j], list[i] |
| i++ |
| j-- |
| } |
| } |
| |
| func _() { |
| // Reverse can be called with an explicit type argument. |
| Reverse[int](nil) |
| Reverse[string]([]string{"foo", "bar"}) |
| Reverse[struct{x, y int}]([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}}) |
| |
| // Since the type parameter is used for an incoming argument, |
| // it can be inferred from the provided argument's type. |
| Reverse([]string{"foo", "bar"}) |
| Reverse([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}}) |
| |
| // But the incoming argument must have a type, even if it's a |
| // default type. An untyped nil won't work. |
| // Reverse(nil) // this won't type-check |
| |
| // A typed nil will work, though. |
| Reverse([]int(nil)) |
| } |
| |
| // Certain functions, such as the built-in `new` could be written using |
| // type parameters. |
| func new[T any]() *T { |
| var x T |
| return &x |
| } |
| |
| // When calling our own `new`, we need to pass the type parameter |
| // explicitly since there is no (value) argument from which the |
| // result type could be inferred. We don't try to infer the |
| // result type from the assignment to keep things simple and |
| // easy to understand. |
| var _ = new[int]() |
| var _ *float64 = new[float64]() // the result type is indeed *float64 |
| |
| // A function may have multiple type parameters, of course. |
| func foo[A, B, C any](a A, b []B, c *C) B { |
| // do something here |
| return b[0] |
| } |
| |
| // As before, we can pass type parameters explicitly. |
| var s = foo[int, string, float64](1, []string{"first"}, new[float64]()) |
| |
| // Or we can use type inference. |
| var _ float64 = foo(42, []float64{1.0}, &s) |
| |
| // Type inference works in a straight-forward manner even |
| // for variadic functions. |
| func variadic[A, B any](A, B, ...B) int { panic(0) } |
| |
| // var _ = variadic(1) // ERROR not enough arguments |
| var _ = variadic(1, 2.3) |
| var _ = variadic(1, 2.3, 3.4, 4.5) |
| var _ = variadic[int, float64](1, 2.3, 3.4, 4) |
| |
| // Type inference also works in recursive function calls where |
| // the inferred type is the type parameter of the caller. |
| func f1[T any](x T) { |
| f1(x) |
| } |
| |
| func f2a[T any](x, y T) { |
| f2a(x, y) |
| } |
| |
| func f2b[T any](x, y T) { |
| f2b(y, x) |
| } |
| |
| func g2a[P, Q any](x P, y Q) { |
| g2a(x, y) |
| } |
| |
| func g2b[P, Q any](x P, y Q) { |
| g2b(y, x) |
| } |
| |
| // Here's an example of a recursive function call with variadic |
| // arguments and type inference inferring the type parameter of |
| // the caller (i.e., itself). |
| func max[T interface{ ~int }](x ...T) T { |
| var x0 T |
| if len(x) > 0 { |
| x0 = x[0] |
| } |
| if len(x) > 1 { |
| x1 := max(x[1:]...) |
| if x1 > x0 { |
| return x1 |
| } |
| } |
| return x0 |
| } |
| |
| // When inferring channel types, the channel direction is ignored |
| // for the purpose of type inference. Once the type has been in- |
| // fered, the usual parameter passing rules are applied. |
| // Thus even if a type can be inferred successfully, the function |
| // call may not be valid. |
| |
| func fboth[T any](chan T) {} |
| func frecv[T any](<-chan T) {} |
| func fsend[T any](chan<- T) {} |
| |
| func _() { |
| var both chan int |
| var recv <-chan int |
| var send chan<-int |
| |
| fboth(both) |
| fboth(recv /* ERROR cannot use */ ) |
| fboth(send /* ERROR cannot use */ ) |
| |
| frecv(both) |
| frecv(recv) |
| frecv(send /* ERROR cannot use */ ) |
| |
| fsend(both) |
| fsend(recv /* ERROR cannot use */) |
| fsend(send) |
| } |
| |
| func ffboth[T any](func(chan T)) {} |
| func ffrecv[T any](func(<-chan T)) {} |
| func ffsend[T any](func(chan<- T)) {} |
| |
| func _() { |
| var both func(chan int) |
| var recv func(<-chan int) |
| var send func(chan<- int) |
| |
| ffboth(both) |
| ffboth(recv /* ERROR cannot use */ ) |
| ffboth(send /* ERROR cannot use */ ) |
| |
| ffrecv(both /* ERROR cannot use */ ) |
| ffrecv(recv) |
| ffrecv(send /* ERROR cannot use */ ) |
| |
| ffsend(both /* ERROR cannot use */ ) |
| ffsend(recv /* ERROR cannot use */ ) |
| ffsend(send) |
| } |
| |
| // When inferring elements of unnamed composite parameter types, |
| // if the arguments are defined types, use their underlying types. |
| // Even though the matching types are not exactly structurally the |
| // same (one is a type literal, the other a named type), because |
| // assignment is permitted, parameter passing is permitted as well, |
| // so type inference should be able to handle these cases well. |
| |
| func g1[T any]([]T) {} |
| func g2[T any]([]T, T) {} |
| func g3[T any](*T, ...T) {} |
| |
| func _() { |
| type intSlize []int |
| g1([]int{}) |
| g1(intSlize{}) |
| g2(nil, 0) |
| |
| type myString string |
| var s1 string |
| g3(nil, "1", myString("2"), "3") |
| g3(& /* ERROR does not match */ s1, "1", myString("2"), "3") |
| _ = s1 |
| |
| type myStruct struct{x int} |
| var s2 myStruct |
| g3(nil, struct{x int}{}, myStruct{}) |
| g3(&s2, struct{x int}{}, myStruct{}) |
| g3(nil, myStruct{}, struct{x int}{}) |
| g3(&s2, myStruct{}, struct{x int}{}) |
| } |
| |
| // Here's a realistic example. |
| |
| func append[T any](s []T, t ...T) []T { panic(0) } |
| |
| func _() { |
| var f func() |
| type Funcs []func() |
| var funcs Funcs |
| _ = append(funcs, f) |
| } |
| |
| // Generic type declarations cannot have empty type parameter lists |
| // (that would indicate a slice type). Thus, generic functions cannot |
| // have empty type parameter lists, either. This is a syntax error. |
| |
| func h[] /* ERROR empty type parameter list */ () {} |
| |
| func _() { |
| h[] /* ERROR operand */ () |
| } |
| |
| // Parameterized functions must have a function body. |
| |
| func _ /* ERROR missing function body */ [P any]() |