This document 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 read first.
The first step towards learning to write good code is to read good code. 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 and follow their example.
Consistency makes programs easy to read. If a program says the same thing twice, it should say it the same way both times. Conversely, if two different sections of a program look different, the reader will expect them to do 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 to do so would be incorrect. A loop that counts down implicitly says “something special is happening here.” A reader who finds a program in which some loops count up and the rest count down will spend time trying to understand why. Don't run loops backwards unless it's necessary.
Loop direction is just one
programming decision which a programmer
may be tempted 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
there?
Why is the Log
constructor CreateLog
when
the List
constructor is NewList
?
Why is this data structure initialized using
a structure literal when that one
is initialized using individual assignments?
And so on.
These questions distract from the important one:
what does the code do?
Moreover, internal consistency is important not only within a single file,
but also within the the surrounding source files.
Being consistent about little things
lets readers concentrate on big ones.
This document describes how to use Go effectively and idiomatically so that a 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 based on style alone.
Formatting issues are the most contentious but the least consequential. People adapt to different formatting styles, even if at first the styles “look weird,” but they shouldn't be asked to. Everyone should use the same formatting; as in English, consistent punctuation and spacing make the text easier to read. Most of the local formatting style can be picked up by reading existing Go programs (see above), but to make them explicit here are some common points.
The local style is to use tabs, not spaces, for indentation.
There should be no trailing white space at the end of lines.
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 an extra tab.
Go does not require parentheses around the expression
following the for
, if
, range
,
switch
, and return
keywords.
Go provides C-style /* */
block comments
and C++-style //
line comments.
Use line comments by default,
reserving block comments for top-level package comments
and commenting out large swaths of code.
If a comment immediately precedes a top-level declaration, the Go documentation server (TODO: that's not a public URL.) uses that comment as the documentation for the constant, function, method, package, type or variable being declared. These are called doc comments. 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. If a name appears multiple times due to forward declarations or appearance in multiple source files within a package, only one instance requires a doc comment, and any one will do.
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 {
The complete English sentence form admits a wider variety of automated presentations.
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 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. */or
/* 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; they contain no markup. Refrain from ASCII embellishment like *this* or /this/.
Go uses the case of the first letter in a name to decide whether the name is visible in other packages. Multiword names use MixedCaps or mixedCaps rather than underscores.
Package names are lower case 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 unique
across all source code.
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.
Letters are easier to distinguish than numbers; use i
and j
not i1
and i2
.
Exported names must convey more information
because they appear far from their origin.
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.
One-method interfaces are conventionally named by
the method name plus the -er suffix: Reader
,
Writer
, Formatter
.
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 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
.
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.
hdr, body, checksum := buf[0:20], buf[20:len(buf)-4], buf[len(buf)-4:len(buf)];
If an if
body doesn't flow off the end of the
body—that is, the body ends in break
, continue
,
goto
, or return
—omit the else
.
f, err := os.Open(name, os.O_RDONLY, 0); if err != nil { return err; } codeUsing(f);
Go's switch
is more general 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,
it will be clearer if rewritten as a switch
.
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 }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 }
Functions are great for factoring out common code, but if a function is only called once, ask whether it is necessary, especially if it is just a short wrapper around another function. This style is rampant in C++ code: wrappers call wrappers that call wrappers that call wrappers. This style hinders people trying to understand the program, not to mention computers trying to execute it.
If a function must return multiple values, it can do so directly. There is no need to pass a pointer to a return value.
Error cases 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.
if len(name) == 0 { return; } if IsDir(name) { return; } f, err := os.Open(name, os.O_RDONLY, 0); if err != nil { return; } codeUsing(f);
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.
os.Error
s 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
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.
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
;
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:
func New(len int) *Vector { return new(Vector).Init(len) }
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.
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 is unaffected by the change of algorithm.
tables
XXX struct tags for marshalling. template eventually datafmt
Do not communicate by sharing memory; instead, share memory by communicating.
XXX, more here.
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.
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.
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.
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.