content: expand discussion of methods

Fixes golang/go#13818

Change-Id: I22817c0c3bfa3d7e5a769f239a2521c293112a43
Reviewed-on: https://go-review.googlesource.com/18246
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/content/methods.article b/content/methods.article
index e032fe0..9319062 100644
--- a/content/methods.article
+++ b/content/methods.article
@@ -6,33 +6,137 @@
 
 * Methods
 
-Go does not have classes. However, you can define methods on struct types.
+Go does not have classes.
+However, you can define methods on types.
 
-The _method_receiver_ appears in its own argument list between the `func` keyword and the method name.
+A method is a function with a special _receiver_ argument.
+
+The receiver appears in its own argument list between the `func` keyword and
+the method name.
+
+In this example, the `Abs` method has a receiver of type `Vertex` named `v`.
 
 .play methods/methods.go
 
+* Methods are functions
+
+Remember: a method is just a function with a receiver argument.
+
+Here's `Abs` written as a regular function with no change in functionality.
+
+.play methods/methods-funcs.go
+
 * Methods continued
 
-You can declare a method on _any_ type that is declared in your package, not just struct types.
+You can declare a method on non-struct types, too.
 
-However, you cannot define a method on a type from another package (including built in types).
+In this example we see a numeric type `MyFloat` with an `Abs` method.
+
+You can only declare a method with a receiver whose type is defined in the same
+package as the method.
+You cannot declare a method with a receiver whose type is defined in another
+package (which includes the built-in types such as `int`).
 
 .play methods/methods-continued.go
 
-* Methods with pointer receivers
+* Pointer receivers
 
-Methods can be associated with a named type or a pointer to a named type.
+You can declare methods with pointer receivers.
 
-We just saw two `Abs` methods. One on the `*Vertex` pointer type and the other on the `MyFloat` value type.
+This means the receiver type has the literal syntax `*T` for some type `T`.
+(Also, `T` cannot itself be a pointer such as `*int`.)
 
-There are two reasons to use a pointer receiver. First, to avoid copying the value on each method call (more efficient if the value type is a large struct). Second, so that the method can modify the value that its receiver points to.
+For example, the `Scale` method here is defined on `*Vertex`.
 
-Try changing the declarations of the `Abs` and `Scale` methods to use `Vertex` as the receiver, instead of `*Vertex`.
+Methods with pointer receivers can modify the value to which the receiver
+points (as `Scale` does here).
+Since methods often need to modify their receiver, pointer receivers are more
+common than value receivers.
 
-The `Scale` method has no effect when `v` is a `Vertex`. `Scale` mutates `v`. When `v` is a value (non-pointer) type, the method sees a copy of the `Vertex` and cannot mutate the original value.
+Try removing the `*` from the declaration of the `Scale` function on line 16
+and observe how the program's behavior changes.
 
-`Abs` works either way. It only reads `v`. It doesn't matter whether it is reading the original value (through a pointer) or a copy of that value.
+With a value receiver, the `Scale` method operates on a copy of the original
+`Vertex` value.
+(This is the same behavior as for any other function argument.)
+The `Scale` method must have a pointer receiver to change the `Vertex` value
+declared in the `main` function.
+
+.play methods/methods-pointers.go
+
+* Pointers and functions
+
+Here we see the `Abs` and `Scale` methods rewritten as functions.
+
+Again, try removing the `*` from line 16.
+Can you see why the behavior changes?
+What else did you need to change for the example to compile?
+
+(If you're not sure, continue to the next page.)
+
+.play methods/methods-pointers-explained.go
+
+* Methods and pointer indirection
+
+Comparing the previous two programs, you might notice that
+functions with a pointer argument must take a pointer:
+
+	var v Vertex
+	ScaleFunc(v)  // Compile error!
+	ScaleFunc(&v) // OK
+
+while methods with pointer receivers take either a value or a pointer as the
+receiver when they are called:
+
+	var v Vertex
+	v.Scale(5)  // OK
+	p := &v
+	p.Scale(10) // OK
+
+For the statement `v.Scale(5)`, even though `v` is a value and not a pointer,
+the method with the pointer receiver is called automatically.
+That is, as a convenience, Go interprets the statement `v.Scale(5)` as
+`(&v).Scale(5)` since the `Scale` method has a pointer receiver.
+
+.play methods/indirection.go
+
+* Methods and pointer indirection (2)
+
+The equivalent thing happens in the reverse direction.
+
+Functions that take a value argument must take a value of that specific type:
+
+	var v Vertex
+	fmt.Println(AbsFunc(v))  // OK
+	fmt.Println(AbsFunc(&v)) // Compile error!
+
+while methods with value receivers take either a value or a pointer as the
+receiver when they are called:
+
+	var v Vertex
+	fmt.Println(v.Abs()) // OK
+	p := &v
+	fmt.Println(p.Abs()) // OK
+
+In this case, the method call `p.Abs()` is interpreted as `(*p).Abs()`.
+
+.play methods/indirection-values.go
+
+* Choosing a value or pointer receiver
+
+There are two reasons to use a pointer receiver.
+
+The first is so that the method can modify the value that its receiver points to.
+
+The second is to avoid copying the value on each method call.
+This can be more efficient if the receiver is a large struct, for example.
+
+In this example, both `Scale` and `Abs` are with receiver type `*Vertex`,
+even though the `Abs` method needn't modify its receiver.
+
+In general, all methods on a given type to should have either value or pointer
+receivers, but not a mixture of both.
+(We'll see why over the next few pages.)
 
 .play methods/methods-with-pointer-receivers.go
 
diff --git a/content/methods/indirection-values.go b/content/methods/indirection-values.go
new file mode 100644
index 0000000..04e022b
--- /dev/null
+++ b/content/methods/indirection-values.go
@@ -0,0 +1,30 @@
+// +build OMIT
+
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+type Vertex struct {
+	X, Y float64
+}
+
+func (v Vertex) Abs() float64 {
+	return math.Sqrt(v.X*v.X + v.Y*v.Y)
+}
+
+func AbsFunc(v Vertex) float64 {
+	return math.Sqrt(v.X*v.X + v.Y*v.Y)
+}
+
+func main() {
+	v := Vertex{3, 4}
+	fmt.Println(v.Abs())
+	fmt.Println(AbsFunc(v))
+
+	p := &Vertex{4, 3}
+	fmt.Println(p.Abs())
+	fmt.Println(AbsFunc(*p))
+}
diff --git a/content/methods/indirection.go b/content/methods/indirection.go
new file mode 100644
index 0000000..6b69828
--- /dev/null
+++ b/content/methods/indirection.go
@@ -0,0 +1,31 @@
+// +build OMIT
+
+package main
+
+import "fmt"
+
+type Vertex struct {
+	X, Y float64
+}
+
+func (v *Vertex) Scale(f float64) {
+	v.X = v.X * f
+	v.Y = v.Y * f
+}
+
+func ScaleFunc(v *Vertex, f float64) {
+	v.X = v.X * f
+	v.Y = v.Y * f
+}
+
+func main() {
+	v := Vertex{3, 4}
+	v.Scale(2)
+	ScaleFunc(&v, 10)
+
+	p := &Vertex{4, 3}
+	p.Scale(3)
+	ScaleFunc(p, 8)
+
+	fmt.Println(v, p)
+}
diff --git a/content/methods/methods-funcs.go b/content/methods/methods-funcs.go
new file mode 100644
index 0000000..476f0cf
--- /dev/null
+++ b/content/methods/methods-funcs.go
@@ -0,0 +1,21 @@
+// +build OMIT
+
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+type Vertex struct {
+	X, Y float64
+}
+
+func Abs(v Vertex) float64 {
+	return math.Sqrt(v.X*v.X + v.Y*v.Y)
+}
+
+func main() {
+	v := Vertex{3, 4}
+	fmt.Println(Abs(v))
+}
diff --git a/content/methods/methods-pointers-explained.go b/content/methods/methods-pointers-explained.go
new file mode 100644
index 0000000..db9107a
--- /dev/null
+++ b/content/methods/methods-pointers-explained.go
@@ -0,0 +1,27 @@
+// +build OMIT
+
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+type Vertex struct {
+	X, Y float64
+}
+
+func Abs(v Vertex) float64 {
+	return math.Sqrt(v.X*v.X + v.Y*v.Y)
+}
+
+func Scale(v *Vertex, f float64) {
+	v.X = v.X * f
+	v.Y = v.Y * f
+}
+
+func main() {
+	v := Vertex{3, 4}
+	Scale(&v, 10)
+	fmt.Println(Abs(v))
+}
diff --git a/content/methods/methods-pointers.go b/content/methods/methods-pointers.go
new file mode 100644
index 0000000..cd88f46
--- /dev/null
+++ b/content/methods/methods-pointers.go
@@ -0,0 +1,27 @@
+// +build OMIT
+
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+type Vertex struct {
+	X, Y float64
+}
+
+func (v Vertex) Abs() float64 {
+	return math.Sqrt(v.X*v.X + v.Y*v.Y)
+}
+
+func (v *Vertex) Scale(f float64) {
+	v.X = v.X * f
+	v.Y = v.Y * f
+}
+
+func main() {
+	v := Vertex{3, 4}
+	v.Scale(10)
+	fmt.Println(v.Abs())
+}
diff --git a/content/methods/methods.go b/content/methods/methods.go
index 8347705..692a095 100644
--- a/content/methods/methods.go
+++ b/content/methods/methods.go
@@ -11,11 +11,11 @@
 	X, Y float64
 }
 
-func (v *Vertex) Abs() float64 {
+func (v Vertex) Abs() float64 {
 	return math.Sqrt(v.X*v.X + v.Y*v.Y)
 }
 
 func main() {
-	v := &Vertex{3, 4}
+	v := Vertex{3, 4}
 	fmt.Println(v.Abs())
 }