effective-go-summary.md raw

Effective Go - Key Points Summary

Source: https://go.dev/doc/effective_go

Formatting

Commentary

Example:

// Package regexp implements regular expression search.
package regexp

// Compile parses a regular expression and returns, if successful,
// a Regexp object that can be used to match against text.
func Compile(str string) (*Regexp, error) {

Names

Package Names

Getters/Setters

Interface Names

MixedCaps

Semicolons

Control Structures

If

if err := file.Chmod(0664); err != nil {
    log.Print(err)
    return err
}

Redeclaration

f, err := os.Open(name)
// err is declared here

d, err := f.Stat()
// err is redeclared here (same scope)

For

// Like a C for
for init; condition; post { }

// Like a C while
for condition { }

// Like a C for(;;)
for { }

// Range over array/slice/map/channel
for key, value := range oldMap {
    newMap[key] = value
}

// If you only need the key
for key := range m {
    // ...
}

// If you only need the value
for _, value := range array {
    // ...
}

Switch

switch {
case '0' <= c && c <= '9':
    return c - '0'
case 'a' <= c && c <= 'f':
    return c - 'a' + 10
case 'A' <= c && c <= 'F':
    return c - 'A' + 10
}

Type Switch

switch t := value.(type) {
case int:
    fmt.Printf("int: %d\n", t)
case string:
    fmt.Printf("string: %s\n", t)
default:
    fmt.Printf("unexpected type %T\n", t)
}

Functions

Multiple Return Values

func (file *File) Write(b []byte) (n int, err error) {
    // ...
}

Named Result Parameters

func ReadFull(r Reader, buf []byte) (n int, err error) {
    for len(buf) > 0 && err == nil {
        var nr int
        nr, err = r.Read(buf)
        n += nr
        buf = buf[nr:]
    }
    return
}

Defer

func trace(s string) string {
    fmt.Println("entering:", s)
    return s
}

func un(s string) {
    fmt.Println("leaving:", s)
}

func a() {
    defer un(trace("a"))
    fmt.Println("in a")
}

Data

Allocation with new

p := new(int)   // p is *int, points to zeroed int

Constructors and Composite Literals

func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    return &File{fd: fd, name: name}
}

Allocation with make

make([]int, 10, 100)   // slice: len=10, cap=100
make(map[string]int)   // map
make(chan int, 10)     // buffered channel

Arrays

Slices

Maps

Printing

Initialization

Constants

init Function

func init() {
    // initialization code
}

Methods

Pointers vs. Values

Rule: Value methods can be called on both values and pointers, but pointer methods should only be called on pointers (though Go allows calling on addressable values).

type ByteSlice []byte

func (slice ByteSlice) Append(data []byte) []byte {
    // ...
}

func (p *ByteSlice) Append(data []byte) {
    slice := *p
    // ...
    *p = slice
}

Interfaces and Other Types

Interfaces

Type Assertions

value, ok := str.(string)

Type Switches

switch v := value.(type) {
case string:
    // v is string
case int:
    // v is int
}

Generality

The Blank Identifier

Unused Imports and Variables

import _ "net/http/pprof"  // Import for side effects

Interface Checks

var _ json.Marshaler = (*RawMessage)(nil)

Embedding

Composition, not Inheritance

type ReadWriter struct {
    *Reader  // *bufio.Reader
    *Writer  // *bufio.Writer
}

Concurrency

Share by Communicating

Goroutines

Channels

ci := make(chan int)            // unbuffered
cj := make(chan int, 0)         // unbuffered
cs := make(chan *os.File, 100)  // buffered

Channels of Channels

type Request struct {
    args        []int
    f           func([]int) int
    resultChan  chan int
}

Parallelization

const numCPU = runtime.NumCPU()
runtime.GOMAXPROCS(numCPU)

Errors

Error Type

type error interface {
    Error() string
}

Custom Errors

type PathError struct {
    Op   string
    Path string
    Err  error
}

func (e *PathError) Error() string {
    return e.Op + " " + e.Path + ": " + e.Err.Error()
}

Panic

Recover

func server(workChan <-chan *Work) {
    for work := range workChan {
        go safelyDo(work)
    }
}

func safelyDo(work *Work) {
    defer func() {
        if err := recover(); err != nil {
            log.Println("work failed:", err)
        }
    }()
    do(work)
}

A Web Server Example

package main

import (
    "fmt"
    "log"
    "net/http"
)

type Counter struct {
    n int
}

func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    ctr.n++
    fmt.Fprintf(w, "counter = %d\n", ctr.n)
}

func main() {
    ctr := new(Counter)
    http.Handle("/counter", ctr)
    log.Fatal(http.ListenAndServe(":8080", nil))
}