Methods
In the previous lesson a struct was a form with labeled fields. Now let's teach the form to act. Imagine that the bottom of a "Rectangle" form says: "Area: multiply the width by the height". Whoever holds a filled-in form knows right away how to work out its area. The action belongs to that kind of form, not to some general handbook.
Let's see why this is useful. Both a rectangle and a square have an area. But you cannot declare two functions named area in one package, even with different parameter types:
package main
type Rect struct{ Width, Height float64 }
func area(r Rect) float64 { return r.Width * r.Height }
type Square struct{ Side float64 }
func area(s Square) float64 { return s.Side * s.Side } // ✗
func main() {}area redeclared in this blockYou could name the functions rectArea and squareArea, but then every operation's name has to carry the type's name. Go ties an operation to the type itself.
Declaring a method
A method is a function with an extra parameter written in parentheses before its name. That parameter is called the receiver. The declaration func (r Rect) Area() float64 reads left to right:
func—the keyword, as for a plain function;(r Rect)—the receiver: the namerand the typeRect;Area—the method's name;()—the ordinary parameters, none here;float64—the result type.
Here are Area methods on both types:
type Rect struct{ Width, Height float64 }
func (r Rect) Area() float64 { return r.Width * r.Height }
type Square struct{ Side float64 }
func (s Square) Area() float64 { return s.Side * s.Side }A method's name belongs to its type, so Rect.Area and Square.Area do not clash. Methods are declared outside the struct's braces, anywhere in the package. A receiver's name is usually short—the first letter of the type: r for Rect, s for Square.
Here the form picture is not exact: the instruction is not printed inside the struct. Inside struct { … } you list only fields, and a method is tied to the type only through its receiver.
Calling a method
A method is called with a dot after a value: r.Area(). The value before the dot becomes the receiver, and inside the method r is an ordinary parameter. The call r.Area() does what a function area(r) would do.
r := Rect{Width: 2, Height: 3}
s := Square{Side: 4}
fmt.Println(r.Area(), s.Area()) // 6 16Let's walk through the calls:
- In
r.Area()the receiver isr, and the method returns2 * 3, that is6. - In
s.Area()it is theSquaremethod that runs, and it returns4 * 4, that is16. fmt.Printlnprints both numbers:6 16.
Go picks the method by the type of the value before the dot.
Method and function
Interviewers like to ask: "How is a method different from a function?" The answer has three points:
- a method is the same kind of function, but with a receiver before its name;
- the receiver ties the method to a type, and the method is called with a dot:
r.Area(); - a function without a receiver is called by its name, with no value before a dot.
Which types get methods
The receiver can be any type declared with type in the same package. Here are the two common cases.
A struct
Most often the receiver is a struct, like Rect and Square above. The method reads its fields through the receiver: r.Width, r.Height.
Your own type based on another
A type such as type Meters float64 works too. It holds the same kind of number as float64, but it is a separate type of your package. That is why it can have its own methods, such as func (m Meters) Km() float64.
This is how you add actions to a number or a string without a one-field struct. But you add methods only to your own types: you cannot add one to int or to a type from another package.
Common mistake
A method is declared on the built-in type int:
func (n int) Double() int { // ✗
return n * 2
}cannot define new methods on non-local type intThe program does not compile: int is not declared in your package. Declare your own type based on int—type Number int—and add the method to it: func (n Number) Double() Number.
Key points
- A method is a function with a receiver:
func (r Rect) Area() float64. - A method is called with a dot,
r.Area(), and the value before the dot becomes the receiver. - Methods of different types can share a name, but two functions in one package cannot.
- You can add a method only to a type from your own package, not to
int.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.