Patterns#
A few idioms Go programs lean on heavily.
Errors as Values#
Functions return error as the last value; callers check it explicitly.
data, err := os.ReadFile("input")
if err != nil {
return fmt.Errorf("read input: %w", err)
}
Defer Cleanup#
defer schedules a call to run when the surrounding function returns.
f, err := os.Open("data")
if err != nil {
return err
}
defer f.Close()
Context Propagation#
Pass a context.Context as the first argument of any call that does I/O,
spawns work, or might need to be canceled.
func Fetch(ctx context.Context, url string) (*Response, error) { ... }
Functional Options#
A common way to build configurable constructors.
type Server struct{ port int; tls bool }
type Option func(*Server)
func WithPort(p int) Option { return func(s *Server) { s.port = p } }
func WithTLS() Option { return func(s *Server) { s.tls = true } }
func New(opts ...Option) *Server {
s := &Server{port: 8080}
for _, o := range opts {
o(s)
}
return s
}
Small Interfaces#
Define interfaces in the package that uses them, and keep them small. The
standard examples are one-method types like io.Reader and io.Writer.
Goroutine + Channel#
Don’t communicate by sharing memory; share memory by communicating. Use
channels to coordinate goroutines, sync.WaitGroup to wait for a group to
finish, and select to multiplex.
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go func(u string) {
defer wg.Done()
fetch(u)
}(url)
}
wg.Wait()