Home

Awesome

Go-advice

(Some of advices are implemented in go-critic)

Contents

Go Proverbs

Author: Rob Pike See more: https://go-proverbs.github.io/

The Zen of Go

Author: Dave Cheney See more: https://the-zen-of-go.netlify.com/

Code

Always go fmt your code.

Community uses the official Go format, do not reinvent the wheel.

Try to reduce code entropy. This will help everyone to make code easy to read.

Multiple if-else statements can be collapsed into a switch

// NOT BAD
if foo() {
	// ...
} else if bar == baz {
	// ...
} else {
	// ...
}

// BETTER
switch {
case foo():
	// ...
case bar == baz:
	// ...
default:
	// ...
}

To pass a signal prefer chan struct{} instead of chan bool.

When you see a definition of chan bool in a structure, sometimes it's not that easy to understand how this value will be used, example:

type Service struct {
	deleteCh chan bool // what does this bool mean? 
}

But we can make it more clear by changing it to chan struct{} which explicitly says: we do not care about value (it's always a struct{}), we care about an event that might occur, example:

type Service struct {
	deleteCh chan struct{} // ok, if event than delete something.
}

Prefer 30 * time.Second instead of time.Duration(30) * time.Second

You don't need to wrap untyped const in a type, compiler will figure it out. Also prefer to move const to the first place:

// BAD
delay := time.Second * 60 * 24 * 60

// VERY BAD
delay := 60 * time.Second * 60 * 24

// GOOD
delay := 24 * 60 * 60 * time.Second

// EVEN BETTER
delay := 24 * time.Hour

Use time.Duration instead of int64 + variable name

// BAD
var delayMillis int64 = 15000

// GOOD
var delay time.Duration = 15 * time.Second

Group const declarations by type and var by logic and/or type

// BAD
const (
	foo     = 1
	bar     = 2
	message = "warn message"
)

// MOSTLY BAD
const foo = 1
const bar = 2
const message = "warn message"

// GOOD
const (
	foo = 1
	bar = 2
)

const message = "warn message"

This pattern works for var too.

defer func() {
	err := ocp.Close()
	if err != nil {
		rerr = err
	}
}()
package main

type Status = int
type Format = int // remove `=` to have type safety

const A Status = 1
const B Format = 1

func main() {
	println(A == B)
}
func f(a int, _ string) {}
func f(a int, b int, s string, p string)
func f(a, b int, s, p string)
var s []int
fmt.Println(s, len(s), cap(s))
if s == nil {
	fmt.Println("nil!")
}
// Output:
// [] 0 0
// nil!
var a []string
b := []string{}

fmt.Println(reflect.DeepEqual(a, []string{}))
fmt.Println(reflect.DeepEqual(b, []string{}))
// Output:
// false
// true
value := reflect.ValueOf(object)
kind := value.Kind()
if kind >= reflect.Chan && kind <= reflect.Slice {
	// ...
}
func f1() {
	var a, b struct{}
	print(&a, "\n", &b, "\n") // Prints same address
	fmt.Println(&a == &b)     // Comparison returns false
}

func f2() {
	var a, b struct{}
	fmt.Printf("%p\n%p\n", &a, &b) // Again, same address
	fmt.Println(&a == &b)          // ...but the comparison returns true
}
const (
	_ = iota
	testvar // will be int
)

vs

type myType int
const (
	_ myType = iota
	testvar // will be myType
)

Don’t use encoding/gob on structs you don’t own.

At some point structure may change and you might miss this. As a result this might cause a hard to find bug.

Don't depend on the evaluation order, especially in a return statement.

// BAD
return res, json.Unmarshal(b, &res)

// GOOD
err := json.Unmarshal(b, &res)
return res, err

To prevent unkeyed literals add _ struct{} field:

type Point struct {
	X, Y float64
	_    struct{} // to prevent unkeyed literals
}

For Point{X: 1, Y: 1} everything will be fine, but for Point{1,1} you will get a compile error:

./file.go:1:11: too few values in Point literal

There is a check in go vet command for this, there is no enough arguments to add _ struct{} in all your structs.

To prevent structs comparison add an empty field of func type

type Point struct {
	_ [0]func()	// unexported, zero-width non-comparable field
	X, Y float64
}

Prefer http.HandlerFunc over http.Handler

To use the 1st one you just need a func, for the 2nd you need a type.

Move defer to the top

This improves code readability and makes clear what will be invoked at the end of a function.

JavaScript parses integers as floats and your int64 might overflow.

Use json:"id,string" instead.

type Request struct {
	ID int64 `json:"id,string"`
}

Concurrency

Performance

b := a[:0]
for _, x := range a {
	if f(x) {
		b = append(b, x)
	}
}

To help compiler to remove bound checks see this pattern _ = b[7]

res, _ := client.Do(req)
io.Copy(ioutil.Discard, res.Body)
defer res.Body.Close()
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
func (entry Entry) MarshalJSON() ([]byte, error) {
	buffer := bytes.NewBufferString("{")
	first := true
	for key, value := range entry {
		jsonValue, err := json.Marshal(value)
		if err != nil {
			return nil, err
		}
		if !first {
			buffer.WriteString(",")
		}
		first = false
		buffer.WriteString(key + ":" + string(jsonValue))
	}
	buffer.WriteString("}")
	return buffer.Bytes(), nil
}
// noescape hides a pointer from escape analysis.  noescape is
// the identity function but escape analysis doesn't think the
// output depends on the input. noescape is inlined and currently
// compiles down to zero instructions.
func noescape(p unsafe.Pointer) unsafe.Pointer {
	x := uintptr(p)
	return unsafe.Pointer(x ^ 0)
}
for k := range m {
	delete(m, k)
}
m = make(map[int]int)

Modules

Build

Testing

func TestSomething(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
}
if runtime.GOARM == "arm" {
	t.Skip("this doesn't work under ARM")
}

Tools

Misc

go func() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGQUIT)
	buf := make([]byte, 1<<20)
	for {
		<-sigs
		stacklen := runtime.Stack(buf, true)
		log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf[:stacklen])
	}
}()
var _ io.Reader = (*MyFastReader)(nil)
var hits struct {
	sync.Mutex
	n int
}
hits.Lock()
hits.n++
hits.Unlock()