blob: bb45c4539933a317e7fcbfcc9dcc4cbbe14739d8 [file] [log] [blame] [view]
Jay73628722017-04-16 15:59:16 -04001Table of Contents
2=================
3
4+ [Introduction](#introduction)
5+ [The Spec](#the-spec)
6+ [Usage](#usage)
7 + [Variables](#variables)
8 + [Slice Elements](#slice-elements)
9 + [Map Elements](#map-elements)
10 + [Interfaces](#interfaces)
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110011
12# Introduction
13
14Method sets of a particular type or value are of particular importance in Go, where the method set determines what interfaces a value implements.
15
16# The Spec
17
18There are two important clauses in the [Go Language Specification](http://golang.org/doc/go_spec.html) about method sets. They are as follows:
19
20[Method Sets](http://golang.org/doc/go_spec.html#Method_sets):
21A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other named ` type T ` consists of all methods with receiver type ` T `. The method set of the corresponding pointer type ` *T ` is the set of all methods with receiver ` *T ` or ` T ` (that is, it also contains the method set of ` T `). Any other type has an empty method set. In a method set, each method must have a unique name.
22
23[Calls](http://golang.org/doc/go_spec.html#Calls):
24A method call ` x.m() ` is valid if the method set of (the type of) ` x ` contains ` m ` and the argument list can be assigned to the parameter list of ` m `. If ` x ` is addressable and ` &x `'s method set contains ` m `, ` x.m() ` is shorthand for ` (&x).m() `.
25
26# Usage
27There are many different cases during which a method set crops up in day-to-day programming. Some of the main ones are when calling methods on variables, calling methods on slice elements, calling methods on map elements, and storing values in interfaces.
28
29## Variables
30In general, when you have a variable of a type, you can pretty much call whatever you want on it. When you combine the two rules above together, the following is valid:
31
Kyle Lemonsb4dd0c82015-06-05 15:44:32 -070032```go
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110033type List []int
Dave Day0d6986a2014-12-10 15:02:18 +110034
35func (l List) Len() int { return len(l) }
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110036func (l *List) Append(val int) { *l = append(*l, val) }
37
38func main() {
39 // A bare value
40 var lst List
41 lst.Append(1)
42 fmt.Printf("%v (len: %d)\n", lst, lst.Len())
43
44 // A pointer value
45 plst := new(List)
46 plst.Append(2)
47 fmt.Printf("%v (len: %d)\n", plst, plst.Len())
48}
49```
50
51Note that both pointer and value methods can both be called on both pointer and non-pointer values. To understand why, let's examine the method sets of both types, directly from the spec:
52
Kyle Lemonsb4dd0c82015-06-05 15:44:32 -070053```go
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110054List
55- Len() int
56
57*List
58- Len() int
59- Append(int)
60```
61
62Notice that the method set for ` List ` does not actually contain ` Append(int) ` even though you can see from the above program that you can call the method without a problem. This is a result of the second spec section above. It implicitly translates the first line below into the second:
63
Kyle Lemonsb4dd0c82015-06-05 15:44:32 -070064```go
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110065lst.Append(1)
66(&lst).Append(1)
67```
68
69Now that the value before the dot is a ` *List `, its method set includes Append, and the call is legal.
70
71To make it easier to remember these rules, it may be helpful to simply consider the pointer- and value-receiver methods separately from the method set. It is legal to call a pointer-valued method on anything that is already a pointer or whose address can be taken (as is the case in the above example). It is legal to call a value method on anything which is a value or whose value can be dereferenced (as is the case with any pointer; this case is specified explicitly in the spec).
72
73## Slice Elements
74Slice elements are almost identical to variables. Because they are addressable, both pointer- and value-receiver methods can be called on both pointer- and value-element slices.
75
76## Map Elements
77Map elements are not addressable. Therefore, the following is an _illegal_ operation:
78
Kyle Lemonsb4dd0c82015-06-05 15:44:32 -070079```go
Artemb891cf32016-03-03 18:51:50 +030080lists := map[string]List{}
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110081lists["primes"].Append(7) // cannot be rewritten as (&lists["primes"]).Append(7)
82```
83
84However, the following is still valid (and is the far more common case):
85
Kyle Lemonsb4dd0c82015-06-05 15:44:32 -070086```go
Artemb891cf32016-03-03 18:51:50 +030087lists := map[string]*List{}
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110088lists["primes"] = new(List)
89lists["primes"].Append(7)
90count := lists["primes"].Len() // can be rewritten as (*lists["primes"]).Len()
91```
92
93Thus, both pointer- and value-receiver methods can be called on pointer-element maps, but only value-receiver methods can be called on value-element maps. This is the reason that maps with struct elements are almost always made with pointer elements.
94
95## Interfaces
96The concrete value stored in an interface is not addressable, in the same way that a map element is not addressable. Therefore, when you call a method on an interface, it must either have an identical receiver type or it must be directly discernible from the concrete type: pointer- and value-receiver methods can be called with pointers and values respectively, as you would expect. Value-receiver methods can be called with pointer values because they can be dereferenced first. Pointer-receiver methods cannot be called with values, however, because the value stored inside an interface has no address. When assigning a value to an interface, the compiler ensures that all possible interface methods can actually be called on that value, and thus trying to make an improper assignment will fail on compilation. To extend the earlier example, the following describes what is valid and what is not:
97
Kyle Lemonsb4dd0c82015-06-05 15:44:32 -070098```go
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110099type List []int
Dave Day0d6986a2014-12-10 15:02:18 +1100100
101func (l List) Len() int { return len(l) }
Andrew Gerrand5bc444d2014-12-10 11:35:11 +1100102func (l *List) Append(val int) { *l = append(*l, val) }
103
104type Appender interface {
105 Append(int)
106}
Dave Day0d6986a2014-12-10 15:02:18 +1100107
Andrew Gerrand5bc444d2014-12-10 11:35:11 +1100108func CountInto(a Appender, start, end int) {
109 for i := start; i <= end; i++ {
110 a.Append(i)
111 }
112}
113
114type Lener interface {
115 Len() int
116}
Dave Day0d6986a2014-12-10 15:02:18 +1100117
Andrew Gerrand5bc444d2014-12-10 11:35:11 +1100118func LongEnough(l Lener) bool {
119 return l.Len()*10 > 42
120}
121
122func main() {
123 // A bare value
124 var lst List
125 CountInto(lst, 1, 10) // INVALID: Append has a pointer receiver
126 if LongEnough(lst) { // VALID: Identical receiver type
127 fmt.Printf(" - lst is long enough")
128 }
129
130 // A pointer value
131 plst := new(List)
132 CountInto(plst, 1, 10) // VALID: Identical receiver type
133 if LongEnough(plst) { // VALID: a *List can be dereferenced for the receiver
134 fmt.Printf(" - plst is long enough")
Dave Day0d6986a2014-12-10 15:02:18 +1100135 }
Andrew Gerrand5bc444d2014-12-10 11:35:11 +1100136}
137```