Methods in Go
| Receivers | |
| Value vs pointer receivers | |
| Fluent setters | |
| The Stringer interface |
Receivers
A method is a function with a receiver: the extra parameter in front of the name that binds the function to a type. Callers then use the familiar dot form, as in r.Area instead of a plain function call.
package main
import "fmt"
type Rect struct {
W int
H int
}
func (r Rect) Area() int {
return r.W * r.H
}
func main() {
r := Rect{W: 3, H: 4}
fmt.Println(r.Area())
}
go run main.go
12
Inside the method the receiver behaves like an ordinary parameter, so r.W reads the width of whoever called Area. Methods keep behavior next to the data it works on.
Value vs pointer receivers
Value receivers work on a copy and suit small read only operations, while pointer receivers share the original and are required for any method that mutates it. As a rule of thumb, use pointer receivers for mutation and value receivers for pure computation.
package main
import "fmt"
type Counter struct {
Value int
}
func (c *Counter) Inc() {
c.Value++
}
func (c Counter) Get() int {
return c.Value
}
func main() {
c := Counter{Value: 0}
c.Inc()
fmt.Println(c.Get())
}
1
Go takes the address for you: calling Inc on the variable c works even though the receiver wants *Counter. Stay consistent within one type and avoid mixing receiver styles without a reason.
Fluent setters
A setter that returns its own receiver lets calls chain into one fluent line: each step configures the value and hands it to the next step. The pattern shines in builders and configuration helpers.
package main
import "fmt"
type Email struct {
To string
Body string
}
func (e *Email) SetTo(to string) *Email {
e.To = to
return e
}
func (e *Email) SetBody(body string) *Email {
e.Body = body
return e
}
func main() {
e := &Email{}
e.SetTo("ada@example.com").SetBody("Hello")
fmt.Println(e.To, e.Body)
}
ada@example.com Hello
Each method returns the same pointer it received, so the chain keeps configuring one shared value. Stop chaining when steps need error checks: plain sequential calls with if statements read better there.
The Stringer interface
The fmt package prints any value whose type defines String returning a string by calling that method automatically. Implementing this one method, known as the Stringer interface, controls how custom types appear in logs and output.
package main
import "fmt"
type Point struct {
X int
Y int
}
func (p Point) String() string {
return fmt.Sprintf("(%d, %d)", p.X, p.Y)
}
func main() {
fmt.Println(Point{X: 1, Y: 2})
}
(1, 2)
Without String the same call would print the raw {1 2} form. The next lesson on interfaces explains how one method can satisfy a shared contract across many types.
Next: Interfaces
Article author: Arthur Isaev