From 94439982b62dee7085aae289e4a6643debd69255 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 25 Jun 2009 09:38:35 -0700 Subject: [PATCH] Effective Go; draft TBR=r OCL=30741 CL=30741 --- doc/effective_go.html | 740 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 740 insertions(+) create mode 100644 doc/effective_go.html 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 @@ + +

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. +

+ + + +