Introduction

Go is a new programming language. It eliminates some of the pitfalls in languages like C++ and Java but introduces other ones. Many remain the same. This page gives tips for writing clear, idiomatic Go code and points out common mistakes to avoid. It augments the language specification and the tutorial, both of which you should be familiar with.

Read

The first step to improving as a writer is to read. This step is as necessary for programming as it is for prose, and it is skipped as often by programmers as by writers. The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Read them.

Be consistent

Consistency makes programs easy to read. If a program says the same thing twice, it should say it the same way both times, so that the parallel structure is clear. Conversely, if two different sections of program look different, the reader will expect them to be doing different things.

Consider for loops. Traditionally, a loop over n elements begins:

for i := 0; i < n; i++ {

Much of the time, the loop could run in the opposite order and still be correct:

for i := n-1; i >= 0; i-- {

The convention in most languages (including Go) is to count up unless doing so would be incorrect. A loop that counts down implicitly says “there's a specific reason to count down here; this situation is special.” A reader who finds a program in which half the loops count up and the other half count down will spend time trying to understand why. A programmer who inserts counting-down loops for variety wastes the reader's time.

Loop direction is hardly the only programming decision which a programmer might want to use to be distinctive: tabs or spaces, choice of variable names, choice of method names, whether a type has a constructor, what tests look like, and on and on. As in the loop example, inconsistency sows confusion, and wastes time. Why is this variable called n here and cnt here? Why is the Log constructor CreateLog when the Vector constructor is NewVector? Why is this data structure initialized using a structure literal when this other one is initialized using individual assignments? Why is this idiom used here but not there? And on and on. These questions distract from the important one: what does the code do? Being consistent about little things lets readers concentrate on big ones.

This document describes how to use Go effectively. Equally important, it describes how to use Go idiomatically, so that a Go programmer seeing your code for the first time can focus on what it does and not why it is inconsistent with typical Go practices. Consistency trumps every item listed below. When editing code, read the surrounding context and try to mimic it as much as possible, even if it disagrees with the rules here. It should not be possible to tell which lines you wrote or edited.

Internal consistency is important in a single file, but source files do not exist in isolation. Code must be consistent with the surrounding source file as well, or the same problems arise.

Formatting

Formatting issues are the most contentious but the least consequential. People adapt quite well to different formatting styles, even if at first the styles “look weird.” The most important consideration is that if everyone uses the same formatting, then a reader picking up an unfamiliar piece of code can focus on what the code does instead of what it looks like. Most of the local formatting style can and should be picked up by reading existing Go programs (see above). The following are the most common issues that Google programmers run into.

Use tabs

The local style is to use tabs, not spaces, for indentation.

Trim trailing white space

Files should not contain trailing white space at the end of lines. The script /home/rsc/bin/g4ws removes trailing whitespace from all open files in the current g4 client.

Don't wrap lines mechanically

Go has no 80-character limit. Don't bother with fancy line wrapping just because a line is wider than a punched card. If you must wrap a line, indent with a single tab.

Omit parentheses

Go does not require parentheses around the expression following the for, if, range, and switch keywords.

Use line comments

Go provides C-style /* */ block comments and C++-style // line comments. The local style is to use line comments by default, reserving block comments for top-level package comments and commenting out large swaths of code.

Write doc comments

If a comment immediately precedes a top-level declaration, the Go documentation server uses that comment as the documentation for the constant, function, package, type or variable being declared. To detach a comment from a declaration, insert a blank line between them.

Every exported (capitalized) name in a program should have a doc comment, as should the package declaration itself.

Doc comments consist of complete English sentences. The first sentence should be a one-sentence summary that starts with the name being declared:

// Quote returns a double-quoted Go string literal
// representing s.  The returned string s uses Go escape
// sequences (\t, \n, \xFF, \u0100) for control characters
// and non-ASCII characters.
func Quote(s string) string {

instead of:

/* not Go style */
// Return a double-quoted Go string literal representing s....
func Quote(s string) string {

The complete English sentence form admits a wider variety of automated presentations.

Avoid ASCII Art

Go programs are meant to read equally well using fixed-width and variable-width fonts. Don't use fancy formattings that depend on fixed-width fonts. In particular, don't assume that a single space is the same width as every other character. If you need to make a columnated table, use tabs to separate the columns and the pretty printer (in progress) will make sure the columns are lined up properly in the output.

If you must use comments to separate sections in a file, use a simple block comment:

/*
 * Helper routines for simplifying the fetching of optional fields of basic type.
 * If the field is missing, they return the zero for the type.
 */

instead of:

/* not Go style */
//////////////////////////////////////////////////////////////////////
// Helper routines for simplifying the fetching of optional fields of basic type.
// If the field is missing, they return the zero for the type.

Comments are text, not HTML, and not any kind of markup. Refrain from ASCII embellishment like *this* or /this/. As usual, read the Go sources for examples.

Names

Use MixedCaps

Go uses the case of the first letter in a name to decide whether the name is visible in other packages. In Go, multiword names use MixedCaps or mixedCaps rather than underscores.

Use short package names

Package names are lowercase single-word names: there should be no need for underscore or mixedCaps. The package name is conventionally the base name of the source directory: the package in src/pkg/container/vector is installed as "container/vector" but has name vector, not container_vector and not containerVector. The package name is only the default name used when importing the package; it need not be a unique identifier.

Avoid lengthy names

A name's length should not exceed its information content. For a function-local variable in scope only for a few lines, the name i conveys just as much information as index or idx and is easier to read. On the same note, i and j are better pair of names for index variables than i1 and i2 (or, worse, index1 and index2), because they are easier to tell apart when reading the program quickly.

Exported names must convey more information, because they appear in a larger variety of contexts. Even so, longer names are not always better, and the package name can help convey information: the buffered Reader is bufio.Reader, not bufio.BufReader. Similarly, once.Do is as precise and evocative as once.DoOrWaitUntilDone, and once.Do(f) reads better than once.DoOrWaitUntilDone(f). Contrary to popular belief, encoding small essays into function names does not make it possible to use them without documentation.

Use the -er convention for interface names

One-method interfaces are conventionally named by the method name plus the -er suffix: Reader, Writer, Formatter. Using an interface name distinct from the method name keeps an anonymous struct field of type Reader from conflicting with its own Read method.

Use canonical names

A few method names—Read, Write, Close, Flush, String—have canonical signatures and meanings. To avoid confusion, don't give your method one of those names unless it has the same signature and meaning. Conversely, if your type implements a method with the same meaning as a method on a well-known type, give it the same name and, equally important, the same signature.

Some function-local variables have canonical names too. Just as i is idiomatic in Go for an index variable, n is idiomatic for a count, b for a []byte, s for a string, r for a Reader, err for an os.Error and so on. Don't mix shorthands: it is especially confusing to have two different variables i and idx, or n and cnt.

Idioms

TODO: Add links to code once godoc can handle it.

Address literals to allocate and initialize

Taking the address of a struct or array literal evaluates to a new instance each time it is evaluated. Use these expressions to avoid the repetition of filling out a data structure.

Use parallel assignment to slice a buffer

hdr, body, checksum := buf[0:20], buf[20:len(buf)], buf[len(buf)-4:len(buf)];

Control Flow

Omit needless else bodies

If an if body doesn't flow off the end of the body—that is, the body ends in break, continue, goto, or return—it is preferable to omit the else.

For example:

f, err := os.Open(name, os.O_RDONLY, 0);
if err != nil {
	return err;
}
codeUsing(f);
f.Close();
moreCode();

is preferable to:

/* not Go style */
if f, err := os.Open(name, os.O_RDONLY, 0); err != nil {
	return err;
} else {
	codeUsing(f);
	f.Close();
}
moreCode();

The first form avoids unnecessary indentation and makes it clear that moreCode() only runs when f.Close() does.

Switch

Go's switch is more powerful than C's. When an if-else if-else chain has three or more bodies, or an if condition has a long list of alternatives, consider rewriting it using switch.

go/src/pkg/bytes/bytes.go:
// Compare returns an integer comparing the two byte arrays lexicographically.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b
func Compare(a, b []byte) int {
    for i := 0; i < len(a) && i < len(b); i++ {
        switch {
        case a[i] > b[i]:
            return 1
        case a[i] < b[i]:
            return -1
        }
    }
    switch {
    case len(a) < len(b):
        return -1
    case len(a) > len(b):
        return 1
    }
    return 0
}
go/src/pkg/http/url.go:
func unhex(c byte) byte {
    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
    }
    return 0
}
go/src/pkg/http/url.go:
func shouldEscape(c byte) bool {
    switch c {
    case ' ', '?', '&', '=', '#', '+', '%':
        return true
    }
    return false
}

Functions

Omit needless wrappers

Functions are great for factoring out common functionality. If a function is only called once, ask whether the function is really necessary, especially if it is just a short wrapper around another function. This style runs rampant in C++ code: wrappers call wrappers that call wrappers that call wrappers. Doing this hinders people trying to understand the program, not to mention computers trying to execute it.

Return multiple values

If a function must return multiple values, it can do so directly. The C “pass in a pointer to a return value” idiom is dead.

Errors

Handle errors first

Errors tend to be simpler than non-error cases, and it helps readability when the non-error flow of control is always down the page. Also, error cases tend to end in jumps, so that there is no need for an explicit else.

f, err := os.Open(name, os.O_RDONLY, 0);
if err != nil {
	return err;
}
codeUsing(f);
f.Close();
moreCode();
is preferable to:
/* not Go style */
f, err := os.Open(name, os.O_RDONLY, 0);
if err == nil {
	codeUsing(f);
	f.Close();
} else {
	return err;
}
moreCode();

Return os.Error, not bool

Few functions have just one failure mode. Instead of returning a boolean to signal success, return an os.Error that describes the failure. Even if there is only one failure mode now, there may be more later.

Return structured errors

Implementations of os.Errors should describe the error but also include context. For example, os.Open returns an os.PathError: /src/pkg/os/file.go:
XXX definition of PathError and .String
PathError's String formats the error nicely and is the usual way the error gets used. Callers that care about the precise error details can use a type switch or a type guard to look for specific errors and then extract details.
XXX example here - MkdirAll

Programmer-defined types

Use NewTypeName for constructors

The constructor for the type pkg.MyType should be named pkg.NewMyType and should return *pkg.MyType. The implementation of NewTypeName often uses the struct allocation idiom.

go/src/pkg/os/file.go:
func NewFile(fd int, name string) *File {
	if file < 0 {
		return nil
	}
	return &File{fd, name, nil, 0}
}

Packages that export only a single type sometimes shorten NewTypeName to New; for example, the vector constructor is vector.New, not vector.NewVector.

A type that is intended to be allocated as part of a larger struct may have an Init method that must be called explicitly. Conventionally, the Init method returns the object being initialized, to make the constructor trivial:

go/src/pkg/container/vector/vector.go:
func New(len int) *Vector {
	return new(Vector).Init(len)
}

Make the zero value meaningful

In Go, newly allocated memory and newly declared variables are zeroed. If a type is intended to be allocated without using a constructor (for example, as part of a larger struct or declared as a local variable), define the meaning of the zero value and arrange for that meaning to be useful.

For example, sync.Mutex does not have an explicit constructor or Init method. Instead, the zero value for a sync.Mutex is defined to be an unlocked mutex.

Interfaces

Accept interface values

buffered i/o takes a Reader, not an os.File. XXX

Return interface values

If a type exists only to implement an interface and has no exported methods beyond that interface, there is no need to publish the type itself. Instead, write a constructor that returns an interface value.

For example, both crc32.NewIEEE() and adler32.New() return type hash.Hash32. Substituting the CRC-32 algorithm for Adler-32 in a Go program requires only changing the constructor call: the rest of the code cannot distinguish the two algorithms.

Use interface adapters to expand an implementation

XXX

Use anonymous fields to incorporate an implementation

XXX

Data-Driven Programming

tables

XXX struct tags for marshalling. template eventually datafmt

Concurrency

Share memory by communicating

Do not communicate by sharing memory; instead, share memory by communicating.

XXX, more here.

Testing

Run tests to completion

Tests should not stop early just because one case has misbehaved. If at all possible, let tests continue, in order to characterize the problem in more detail. For example, it is more useful for a test to report that isPrime gives the wrong answer for 2, 3, 5, and 7 (or for 2, 4, 8, and 16) than to report that isPrime gives the wrong answer for 2 and therefore no more tests were run.

Print useful errors when tests fail

If a test fails, print a concise message explaining the context, what happened, and what was expected. Many testing environments encourage causing the program to crash, but stack traces and core dumps have low signal to noise ratios and require reconstructing the situation from scratch. The programmer who triggers the test failure may be someone editing the code months later or even someone editing a different package on which the code depends. Time invested writing a good error message now pays off when the test breaks later.

Use data-driven tests

Many tests reduce to running the same code multiple times, with different input and expected output. Instead of using cut and paste to write this code, create a table of test cases and write a single test that iterates over the table. Once the table is written, you might find that it serves well as input to multiple tests. For example, a single table of encoded/decoded pairs can be used by both TestEncoder and TestDecoder.

This data-driven style dominates in the Go package tests.

Use reflect.DeepEqual to compare complex values

The reflect.DeepEqual function tests whether two complex data structures have equal values. If a function returns a complex data structure, reflect.DeepEqual combined with table-driven testing makes it easy to check that the return value is exactly as expected.