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  

View as plain text