Source file src/internal/types/testdata/examples/methods.go
1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This file shows some examples of methods on type-parameterized types. 6 7 package p 8 9 // Parameterized types may have methods. 10 type T1[A any] struct{ a A } 11 12 // When declaring a method for a parameterized type, the "instantiated" 13 // receiver type acts as an implicit declaration of the type parameters 14 // for the receiver type. In the example below, method m1 on type T1 has 15 // the receiver type T1[A] which declares the type parameter A for use 16 // with this method. That is, within the method m1, A stands for the 17 // actual type argument provided to an instantiated T1. 18 func (t T1[A]) m1() A { return t.a } 19 20 // For instance, if T1 is instantiated with the type int, the type 21 // parameter A in m1 assumes that type (int) as well and we can write 22 // code like this: 23 var x T1[int] 24 var _ int = x.m1() 25 26 // Because the type parameter provided to a parameterized receiver type 27 // is declared through that receiver declaration, it must be an identifier. 28 // It cannot possibly be some other type because the receiver type is not 29 // instantiated with concrete types, it is standing for the parameterized 30 // receiver type. 31 func (t T1[[ /* ERROR "must be an identifier" */ ]int]) m2() {} 32 33 // Note that using what looks like a predeclared identifier, say int, 34 // as type parameter in this situation is deceptive and considered bad 35 // style. In m3 below, int is the name of the local receiver type parameter 36 // and it shadows the predeclared identifier int which then cannot be used 37 // anymore as expected. 38 // This is no different from locally re-declaring a predeclared identifier 39 // and usually should be avoided. There are some notable exceptions; e.g., 40 // sometimes it makes sense to use the identifier "copy" which happens to 41 // also be the name of a predeclared built-in function. 42 func (t T1[int]) m3() { var _ int = 42 /* ERRORx `cannot use 42 .* as int` */ } 43 44 // The names of the type parameters used in a parameterized receiver 45 // type don't have to match the type parameter names in the declaration 46 // of the type used for the receiver. In our example, even though T1 is 47 // declared with type parameter named A, methods using that receiver type 48 // are free to use their own name for that type parameter. That is, the 49 // name of type parameters is always local to the declaration where they 50 // are introduced. In our example we can write a method m2 and use the 51 // name X instead of A for the type parameter w/o any difference. 52 func (t T1[X]) m4() X { return t.a } 53 54 // If the receiver type is parameterized, type parameters must always be 55 // provided: this simply follows from the general rule that a parameterized 56 // type must be instantiated before it can be used. A method receiver 57 // declaration using a parameterized receiver type is no exception. It is 58 // simply that such receiver type expressions perform two tasks simultaneously: 59 // they declare the (local) type parameters and then use them to instantiate 60 // the receiver type. Forgetting to provide a type parameter leads to an error. 61 func (t T1 /* ERRORx `generic type .* without instantiation` */ ) m5() {} 62 63 // However, sometimes we don't need the type parameter, and thus it is 64 // inconvenient to have to choose a name. Since the receiver type expression 65 // serves as a declaration for its type parameters, we are free to choose the 66 // blank identifier: 67 func (t T1[_]) m6() {} 68 69 // Naturally, these rules apply to any number of type parameters on the receiver 70 // type. Here are some more complex examples. 71 type T2[A, B, C any] struct { 72 a A 73 b B 74 c C 75 } 76 77 // Naming of the type parameters is local and has no semantic impact: 78 func (t T2[A, B, C]) m1() (A, B, C) { return t.a, t.b, t.c } 79 func (t T2[C, B, A]) m2() (C, B, A) { return t.a, t.b, t.c } 80 func (t T2[X, Y, Z]) m3() (X, Y, Z) { return t.a, t.b, t.c } 81 82 // Type parameters may be left blank if they are not needed: 83 func (t T2[A, _, C]) m4() (A, C) { return t.a, t.c } 84 func (t T2[_, _, X]) m5() X { return t.c } 85 func (t T2[_, _, _]) m6() {} 86 87 // As usual, blank names may be used for any object which we don't care about 88 // using later. For instance, we may write an unnamed method with a receiver 89 // that cannot be accessed: 90 func (_ T2[_, _, _]) _() int { return 42 } 91 92 // Because a receiver parameter list is simply a parameter list, we can 93 // leave the receiver argument away for receiver types. 94 type T0 struct{} 95 func (T0) _() {} 96 func (T1[A]) _() {} 97 98 // For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). 99 // // A generic receiver type may constrain its type parameter such 100 // // that it must be a pointer type. Such receiver types are not 101 // // permitted. 102 // type T3a[P interface{ ~int | ~string | ~float64 }] P 103 // 104 // func (T3a[_]) m() {} // this is ok 105 // 106 // type T3b[P interface{ ~unsafe.Pointer }] P 107 // 108 // func (T3b /* ERROR "invalid receiver" */ [_]) m() {} 109 // 110 // type T3c[P interface{ *int | *string }] P 111 // 112 // func (T3c /* ERROR "invalid receiver" */ [_]) m() {} 113