Methods
A method is a function attached to a type. It's declared like a function but with an extra parameter, the receiver, written before the name. Methods give your types behaviour — account.Deposit(50), rect.Area() — and they're how types satisfy interfaces. This lesson covers declaring methods, value versus pointer receivers, methods on non-struct types, and method values.
Declaring a method
package main import ( "fmt" "math" ) type Rect struct { Width, Height float64 } func (r Rect) Area() float64 { return r.Width * r.Height } func (r Rect) Perimeter() float64 { return 2 * (r.Width + r.Height) } type Circle struct{ Radius float64 } func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius } func main() { r := Rect{Width: 3, Height: 4} fmt.Println(r.Area(), r.Perimeter()) c := Circle{Radius: 1} fmt.Printf("%.3f\n", c.Area()) }
12 14 3.142
(r Rect) is the receiver: inside the method, r is the value the method was called on. Different types can have methods with the same name — Rect.Area and Circle.Area don't clash. Receiver names are short by convention, usually one or two letters taken from the type name, and the same name is used in every method of the type (never this or self).
Pointer receivers
A method with a value receiver works on a copy, so it can't change the original. To modify the receiver, use a pointer receiver, (a *Account):
package main import ( "errors" "fmt" ) type Account struct { Owner string Balance int } func (a *Account) Deposit(amount int) { a.Balance += amount } func (a *Account) Withdraw(amount int) error { if amount > a.Balance { return errors.New("insufficient funds") } a.Balance -= amount return nil } func (a Account) Summary() string { // reads only: a value receiver works return fmt.Sprintf("%s has %d", a.Owner, a.Balance) } func main() { acc := Account{Owner: "Ana"} acc.Deposit(100) // Go takes &acc for you fmt.Println(acc.Withdraw(30), acc.Withdraw(500)) fmt.Println(acc.Summary()) }
<nil> insufficient funds Ana has 70
You call pointer methods on an ordinary variable: Go automatically takes its address, so acc.Deposit(100) means (&acc).Deposit(100). The reverse also works — calling a value method on a pointer dereferences it.
Value or pointer receiver?
Here's what goes wrong with a value receiver on a method that's meant to change things:
package main import "fmt" type Counter struct{ n int } func (c Counter) IncBroken() { c.n++ } // increments a copy func (c *Counter) Inc() { c.n++ } func main() { var c Counter c.IncBroken() c.IncBroken() fmt.Println(c.n) c.Inc() c.Inc() fmt.Println(c.n) }
0 2
The guidelines:
- Use a pointer receiver if the method changes the receiver, if the struct is large, or if it contains something that mustn't be copied, such as a
sync.Mutex. - Use a value receiver for small, immutable values such as
Point,Moneyortime.Time. - Be consistent: if any method of a type needs a pointer receiver, give all its methods pointer receivers.
Methods on any named type
Methods aren't limited to structs. Any type you define in your package can have them — a named number, string, slice or map:
package main import ( "fmt" "strings" ) type Celsius float64 func (c Celsius) Fahrenheit() float64 { return float64(c)*9/5 + 32 } type Path []string func (p Path) String() string { return "/" + strings.Join(p, "/") } func (p Path) Parent() Path { if len(p) == 0 { return p } return p[:len(p)-1] } func main() { t := Celsius(25) fmt.Println(t.Fahrenheit()) p := Path{"home", "ana", "notes"} fmt.Println(p.String(), p.Parent().String()) fmt.Println(p) // fmt uses the String method automatically }
77 /home/ana/notes /home/ana /home/ana/notes
You can only declare methods on types defined in the same package, so you can't add a method to int or time.Time directly — define your own type based on them instead. A String() string method is special: fmt uses it to print the value (lesson 42).
Methods on nil receivers
A method with a pointer receiver can be called on a nil pointer. That lets a type treat nil as a valid, empty value:
package main import "fmt" type List struct { Value int Next *List } func (l *List) Sum() int { if l == nil { return 0 } return l.Value + l.Next.Sum() } func main() { list := &List{1, &List{2, &List{3, nil}}} fmt.Println(list.Sum()) var empty *List fmt.Println(empty.Sum()) }
6 0
Method values and expressions
value.Method without parentheses gives a function bound to that value. Type.Method gives a function that takes the receiver as its first argument:
package main import "fmt" type Greeter struct{ Greeting string } func (g Greeter) Greet(name string) string { return g.Greeting + ", " + name } func main() { hi := Greeter{"Hi"}.Greet // method value: bound to this Greeter fmt.Println(hi("Ana")) greet := Greeter.Greet // method expression: receiver becomes a parameter fmt.Println(greet(Greeter{"Hello"}, "Ben")) }
Hi, Ana Hello, Ben
Try it yourself
Give a Stack type Push, Pop and Len methods. Which ones need pointer receivers?
3 c b a