From 3e7407915126942f902d86d94430efd7bd7a1cef Mon Sep 17 00:00:00 2001
From: Rob Pike
-Go is a new language. Although it's in the C family
-it has some unusual properties that make effective Go programs
-different in character from programs in existing languages.
+Go is a new language. Although it borrows ideas from
+existing languages,
+it has unusual properties that make effective Go programs
+different in character from programs in its relatives.
A straightforward translation of a C++ or Java program into Go
-is unlikely to produce a satisfactory result—Java programs
+is unlikely to produce a satisfactory result—Java programs
are written in Java, not Go.
On the other hand, thinking about the problem from a Go
perspective could produce a successful but quite different
@@ -257,8 +258,7 @@ var (
Names are as important in Go as in any other language.
In some cases they even have semantic effect: for instance,
the visibility of a name outside a package is determined by whether its
-first character is an upper case letter,
-while methods are looked up by name alone (although the type must match too).
+first character is an upper case letter.
It's therefore worth spending a little time talking about naming conventions
in Go programs.
Introduction
@@ -310,7 +312,7 @@ Moreover,
because imported entities are always addressed with their package name, bufio.Reader
does not conflict with io.Reader
.
Similarly, the constructor for vector.Vector
-could be called NewVector
but since
+would normally be called NewVector
but since
Vector
is the only type exported by the package, and since the
package is called vector
, it's called just New
,
which clients of the package see as vector.New
.
@@ -352,7 +354,7 @@ call your string-converter method String
not ToString
.
-Finally, the convention in Go is to used MixedCaps
+Finally, the convention in Go is to use MixedCaps
or mixedCaps
rather than underscores to write
multiword names.
for
;
and there are new control structures including a type switch and a
multiway communications multiplexer, select
.
-The syntax is also slightly different: parentheses are not part of the syntax
+The syntax is also slightly different:
+parentheses are not required
and the bodies must always be brace-delimited.
@@ -524,7 +527,7 @@ it all up for you:
var m map[string] int; sum := 0; -for key, value := range m { // key is unused; could call it '_' +for _, value := range m { // key is unused sum += value }@@ -633,7 +636,7 @@ func Compare(a, b []byte) int {
One of Go's unusual properties is that functions and methods
can return multiple values. This feature can be used to
-improve on a couple of clumsy idioms in C program: in-band
+improve on a couple of clumsy idioms in C programs: in-band
error returns (-1
for EOF
for example)
and modifying an argument.
A similar approach obviates the need to pass a pointer to a return -value to overwrite an argument. Here's a simple-minded function to +value to simulate a reference parameter. +Here's a simple-minded function to grab a number from a position in a byte array, returning the number and the next position.
@@ -693,7 +697,7 @@ You could use it to scan the numbers in an input arraya
like this:
The return or result "parameters" of a Go function can be given names and
used as regular variables, just like the incoming parameters.
-When named, they are initialized to the zero for their type when
+When named, they are initialized to the zero values for their types when
the function begins; if the function executes a return
statement
with no arguments, the current values of the result parameters are
used as the returned values.
@@ -825,8 +829,9 @@ func NewFile(fd int, name string) *File {
Note that it's perfectly OK to return the address of a local variable;
the storage associated with the variable survives after the function
returns.
-In fact, as a special case, the address of a composite literal
-allocates a fresh instance each time, we can combine these last two lines:
+In fact, taking the address of a composite literal
+allocates a fresh instance each time it is evaluated,
+so we can combine these last two lines:
@@ -972,10 +977,84 @@ But even this style isn't idiomatic Go. Slices are.Slices
-Slices wrap arrays to give a more general, powerful, and convenient interface to sequences -of data. -Except for items with explicit dimension such as rotation matrices, most -array programming in Go is done with slices rather than simple arrays. +Slices wrap arrays to give a more general, powerful, and convenient +interface to sequences of data. Except for items with explicit +dimension such as transformation matrices, most array programming in +Go is done with slices rather than simple arrays. +
++Slices are reference types, which means that if you assign one +slice to another, both refer to the same underlying array. For +instance, if a function takes a slice argument, changes it makes to +the elements of the slice will be visible to the caller, analogous to +passing a pointer to the underlying array. A
+Read
+function can therefore accept a slice argument rather than a (pointer +to an) array and a count; the length within the slice sets an upper +limit of how much data to read. Here is the signature of the +Read
method of theFile
type in package +os
: ++func (file *File) Read(buf []byte) (n int, err os.Error) +++The method returns the number of bytes read and an error value, if +any. To read into the first 32 bytes of a larger buffer +
+b
, slice (here used as a verb) the buffer: ++ n, err := f.Read(buf[0:32]); +++Such slicing is common and efficient. In fact, leaving efficiency aside for +the moment, this snippet would also read the first 32 bytes of the buffer: +
++ var n int; + var err os.Error; + for i := 0; i < 32; i++ { + nbytes, e := f.Read(buf[i:i+1]); + if nbytes == 0 || e != nil { + err = e; + break; + } + n += nbytes; + } +++The length of a slice may be changed as long as it still fits within +the limits of the underyling array; just assign it to a slice of +itself. The capacity of a slice, accessible by the built-in +function
+cap
, reports the maximum length the slice may +assume. Here is a function to append data to a slice. If the data +exceeds the capacity, the slice is reallocated. The +resulting slice is returned. The function uses the fact that +len
andcap
are legal when applied to the +nil
slice, and return 0. ++func Append(slice, data[]byte) []byte { + l := len(slice); + if l + len(data) > cap(slice) { // reallocate + // Allocate double what's needed, for future growth. + newSlice := make([]byte, (l+len(data))*2); + // Copy data (could use bytes.Copy()). + for i, c := range slice { + newSlice[i] = c + } + slice = newSlice; + } + slice = slice[0:l+len(data)]; + for i, c := range data { + slice[l+i] = c + } + return slice; +} +++We must return the slice afterwards because, although
@@ -983,10 +1062,71 @@ array programming in Go is done with slices rather than simple arrays.Append
+can modify the elements ofslice
, the slice itself (the run-time data +structure holding the pointer, length, and capacity) is passed by value.Printing
Methods
-Basics
-Pointers vs. Values
-Methods on arbitrary types
+Pointers vs. Values
++Methods can be defined for any named type except pointers and interfaces; +the receiver does not have to be a struct. +
+In the discussion of slices above, we wrote an
+Append
+function. We can define it as a method on slices instead. To do +this, we first declare a named type to which we can bind the method, and +then make the receiver for the method a value of that type. ++type ByteSlice []byte + +func (slice ByteSlice) Append(data []byte) []slice { + // Body exactly the same as above +} +++This still requires the method to return the updated slice. We can +eliminate that clumsiness by redefining the method to take a +pointer to a
+ByteSlice
as its receiver, so the +method can overwrite the caller's slice. ++func (p *ByteSlice) Append(data []byte) { + slice := *p; + // Body as above, without the return. + *p = slice; +} +++In fact, we can do even better. If we modify our function so it looks +like a standard
+Write
method, like this, ++func (p *ByteSlice) Write(data []byte) (n int, err os.Error) { + slice := *p; + // Again as above. + *p = slice; + return len(data), nil) +} +++then the type
+*ByteSlice
satisfies the standard interface +io.Writer
, which is handy. For instance, we can +print into one: ++ var b ByteSlice; + fmt.Fprintf(&b, "This minute has %d seconds\n", 61); +++Notice that we must pass the address of a
+ByteSlice
+because only*ByteSlice
satisfiesio.Writer
. +The rule about pointers vs. values for receivers is that value methods +can be invoked on pointers and values, but pointer methods can only be +invoked on pointers. This is because pointer methods can modify the +receiver; invoking them on a copy of the value would cause those +modifications to be discarded. ++By the way, the idea of using
Write
on a slice of bytes +is implemented bybytes.Buffer
. +More to come
@@ -1269,7 +1409,7 @@ is initialized using individual assignments? 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. +but also within the surrounding source files. When editing code, read the surrounding context and try to mimic it as much as possible, even if it disagrees with the rules here. @@ -1279,4 +1419,13 @@ Consistency about little things lets readers concentrate on big ones. ++TODO +verifying implementation +type Color uint32 + +// Check that Color implements image.Color and image.Image +var _ image.Color = Black +var _ image.Image = Black +-->