diff --git a/doc/effective_go.html b/doc/effective_go.html new file mode 100644 index 00000000000..1e925528d0a --- /dev/null +++ b/doc/effective_go.html @@ -0,0 +1,740 @@ + +
+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. +
+ ++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. +
+ ++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 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. +
+ ++The local style is to use tabs, not spaces, for indentation. +
+ +
+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.
+
+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. +
+ +Go does not require parentheses around the expression
+following the for
, if
, range
,
+and switch
keywords.
+
+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.
+
+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. +
+ + ++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. +
+ ++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. +
+ +
+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.
+
+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.
+
+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.
+
+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
.
+
+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)], 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
—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.
+
+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
.
+
+// 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 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. +
+ ++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 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(); ++ +
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
;
+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:
+
+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 cannot distinguish the two algorithms.
+
+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.
+