The Go Programming Language

Alan A. A. Donovan / Brian W. Kernighan

Chapter 2: Program Structure


If an entity is declared within a function, it is local to that function. If declared outside a function, however, it is visible in all files of the package to which it belongs. The case of the first letter of a name determines its visibility across package boundaries. If the name begins with an upper-case letter, it is exported, which means it is visible and accessible outside of its own package and may be referred to by other parts of the program, as with Printf in fmt package. Package names themselves are always in lower case.

Generally, the larger the scope of a name, the longer and more meaningful it should be.

Stylistically, Go programmers use “camel case” when forming names by combining words. The letters of acronyms and initialisms like ASCII and HTML are always rendered in the same case.


A declaration names a program entity and specifies some or all of its properties. There are four major kinds of declarations: var, const, type and func.

The package declaration is followed by any import declarations, and then a sequence of package-level declarations of types, variables, constants and functions, in any order.


Each variable declaration has the general form

var name type = expression

Either the type or the = expression part may be omitted, but not both. If the type is omitted, it is determined by the initializer expression. If the expression is omitted, the initial value is the zero value of the type, which is 0 for numbers, false for booleans, "" for strings, and nil for reference types(slice, pointer, map, channel, function). The zero value of an aggregate type like an array or a struct has the zero value of all of its elements or fields.

The zero-value mechanism ensures that a variable always holds a well-defined value of its type; in Go there’s no such thing as an uninitialized value.

Initializers may be literal values or arbitrary expressions. Package-level variables are initialized before main begins, and local variables are initialized as their declarations are encountered during function execution.

Within a function, an alternate form called a short variable declaration may be used to declare and initialize local variables. It takes the form name := expression, and the type of name is determined by the type of expression.

Because of their brevity and flexibility, short variable declarations are used to declare and initialize the majority of local variables. A var declaration tends to be reserved for local variables that need an explicit type that differs from that of the initializer expression, or for when the variable will be assigned a value later and its initial value is unimportant.

i := 100 // an int
var boiling float64 := 100 // a float64


A variable is a piece of storage containing a value. Variables created by declarations are identified by name, such as x, but many variables are only identified by expressions like x[i] or x.f. All these expressions read the value of a variable, except when they appear on the left-hand side of an assignment, in which case a new value is assigned to the variable.

A pointer value is the address of a variable. A pointer is thus the location at which a value is stored. Not every value has an address, but every variable does. With a pointer, we can read or update the value of a variable indirectly, without using or even knowing the name of a variable, if indeed it has a name.

If a variable is declared var x int, the expression &x (“address of x”) yields a pointer to an integer variable, that is, a value of type *int, which is pronounced “pointer to int”. If this value is called p, we say “p points to x”, or equivalently “p contains the address of x.” The variable to which p points is written *p. The expression *p yields the value of that variable, an int, but since *p denotes a variable, it may also appear on the left-hand side of an assignment, in which case the assignment updates the variable.

Each component of a variable of aggregate type–a field of a struct or an element of an array–is also a variable and thus has an address too.

Variables are sometimes described as addressable values. Expressions that denote variables are the only expressions to which the address-of operator & may be applied.

The zero value for a pointer of any type is nil. The test p != nil is true if p points to a variable. Pointers are comparable; two pointers are equal if and only if they point to the same variable or both are nil.

It is perfectly safe for a function to return the address of a local variable. For instance, in the code below, the local variable v created by the particular call to f will remain in existence even after the call has returned, and the pointer p will still refers to it:

var p = f()
function f() *int {
    v := i
    return &v

Each call of f returns a distinct value:

fmt.Println(f() == f()) // "false"

Lifetime of Variables

The lifetime of a variable is the interval of time during which it exists as the program executes. The lifetime of a package-level variable is the entire execution of the program. By contrast, local variables have dynamic lifetimes: a new instance is created each time the declaration statement is executed, and the variable lives on until it becomes unreachable, at which point its storage may be recycled. Function parameters and results are local variables too; they are created each time their enclosing function is called.

How does the garbage collector know that a variable’s storage can be reclaimed? The full story is much more detailed that we need here, but the idea is that the package-level variable, and every local variable of each currently active function, can potentially be the start or the root of the path to the variable in question, following pointers and other kinds of references that ultimately lead to the variable. If no such path exists, the variable has become unreachable, so it can no longer affect the rest of the computation.

Because the lifetime of a variable is determined only by whether or not it is reachable, a local variable may outlive a single iteration of the enclosing loop. It may continue to exist even after its enclosing function has returned.


An assignment, explicit or implicit, is always legal if the left-hand side(the variable) and the right-hand side(the value) have the same type. More generally, the assignment is legal only if the value is assignable to the type of the variable.

The rule for assignability has cases for various types, so we will explain the relevant case as we introduce each new type. For the types we’ve discussed so far, the rules are simple: the types must exactly match, and nil may be assigned to any variable of interface or reference type.

Whether two values may be compared with == or != is related to assignability: in any comparison, the first operand must be assignable to the type of the second operand, or vice versa.

Type Declarations

A type declaration defines a new named type that has the same underlying type as an existing type. The named type provides a way to separate different and perhaps incompatible uses of the underlying type so that they can’t be mixed unintentionally.

type name underlying-type

Different name types with the same underlying type are not the same type, so they cannot be compared or combined with arithmetic expressions.

For every type T, there is a corresponding operation T(x) that converts the value x to type T. A conversion from one type to another is allowed if both have the same underlying type, or if both are unnamed pointer types(?) that point to variables of the same underlying type; these conversions change the type but not the representation of the value.

Conversions are also allowed between numeric types, and between string and some slice types. These conversions may change the representation of the value. For instance, converting a floating-point number to an integer discard any fractional part, and converting a string to a []byte slice allocates a copy of the string data.

The underlying type of a named type determines its structure and representation, and also the set of intrinsic operations it supports, which are the same as if the underlying type had been used directly.

Comparison operator like == and < can also be used to compare a value of a named type to another of the same type, or to a value of the underlying type(?). But two values of different named types cannot be compared directly:

type Celcius int
var a int = 3
var b Celcius = 3
b = a // "cannot use a (type int) as type Celcius in assignment"
fmt.Println(b == a) // "invalid operation: b == a (mismatched types Celcius and int)"
fmt.Println(b == 3) // "true"

Packages and Files

Packages in Go serves the same purposes as libraries or modules in other languages, supporting modularity, encapsulation, separate compilation, and reuse. The source code for a package resides in one or more .go files, usually in a directory whose name ends with the import path; for instance, the files of the packages are stored in directory $GOPATH/src/

Each package serves as a separate name space for its declarations. Within the image package, for example, the identifier Decode refers to a different function than does the same identifier in the unicode/utf16 package. To refer to a function fro m outside its package, we must qualify the identifier to make explicit whether we mean image.Decode or utf16.Decode.

Packages also let us hide information by controlling which names are visible outside the package, or exported. In Go, a simple rule governs which identifiers are exported and which are not: exported identifiers start with an upper-case letter.


Wi thin a Go program, every package is identified by a unique string called its import path. These are the strings that appear in an import declaration like “”. The language specification doesn’t define where these strings come from or what they mean; it’s up to the tools to interpret them. When using the go tool (Chapter 10), an import path denotes a directory containing one or more Go source files that together make up the package.

In addition to its import path, each package has a package name, which is the short (and not necessarily unique) name that appears in its package declaration. By convention, a package’s name matches the last segment of its import path, making it easy to predict that the package name of is tempconv.

Chapter 3: Basic Data Types

Go’s types fall into four categories: basic types, aggregate types, reference types, and interface types. Basic types include numbers, strings, and booleans. Aggregate types–arrays and structs–form more complicated data types by combining values of several simpler ones. Reference types are a diverse group that includes pointers, slice, maps, functions, and channels, but what they have in common is that they are refer to program variables and state indirectly, so that the effect of a operation applied to one reference is observed by all copies of that reference.


There are two types called int and uint that are the natural or the most efficient size for signed and unsigned integers on a particular platform; int is by far the most widely used numeric type. Both these types have the same size, either 32 or 64 bits, but one must not make assumptions about which; different compilers may make different choices even on identical hardware.

The type rune is synonym for int32 and conventionally indicates that a value is a Unicode code point. The two names may be used interchangeably. Similarly, the type byte is a synonym for uint8, and emphasizes that the value is a piece of raw data rather a small numeric quantity.

Regardless of their size, int, uint, and uintptr are different types from their explicitly sized siblings. Thus int is not the same type as int32, even if the natural size of int is 32 bits, and an explicit conversion is required to use and int value where an int32 is needed, and vice versa.

Go’s binary operators for arithmetic, logic, and comparison are listed here in the order of decreasing precedence:

* / % << >> & &^
+ - | ^
== != < <= > >=

There are only five levels of precedence for binary operators. Operators at the same level associate to the left.

The integer arithmetic operators +, -, *, / may be applied to integers, floating-point, and complex numbers, but the remainder operator % applies only to integers. The behavior of % for negative numbers varies across programming languages. In Go, the sign of the remainder is always the same as the sign of the dividend, so -5%-3 and -5/3 are both -2. The behavior of / depends on whether its operands are integers, so 5.0/4.0 is 1.25, but 5/4 is 1 because integer division truncates the result toward zero.

If the result of an arithmetic operation, whether signed or unsigned, has more bits than can be represented in the result type, it is said to overflow. The high-order bits that do not fit are silently discarded. If the original number is a signed type, the result could be negative if the leftmost bit is a 1, as in the int8 example here:

var u uint8 = 255
fmt.Println(u, u+1) // "255, 0"

var i int8 = 127
fmt.Println(u, u+1) // "127 -128"

In general, an explicit conversion is required to convert a value from one type to another, and binary operators for arithmetic and logic (except shifts) must have operands of the same type. Although this occasionally results in longer expressions, it also eliminates a whole class of problems and makes programs easier to understand.


A value of type bool, or boolean, has only two possible values, true and false. The conditions in if and for statements are booleans, and comparison operators like == and < produce a boolean result.

There is no implicit conversion from a boolean value to a numeric value like 0 and 1, or vice versa.


A string is an immutable sequence of bytes. The built-in len function return the number of bytes (not runes) in a string, and the index operation s[i] retrieves the i-th byte of string s, where 0 <= i < len(s).

The i-th byte of a string is not necessarily the i-th character of a string, because the UTF-8 encoding of a non-ASCII code point require two or more bytes.

String values are immutable: the byte sequence contained in a string value can never be changed, though of course we can assign a new value to a string variable.

Immutability means that it is safe for two copies of a string to share the same underlying memory, making it cheap to copy strings of any length. Similarly, a string s and a substring s[7:] may safely share the same data, so the substring operation is also cheap. No new memory is allocated in either case.

String Literals

A string value can be written as a string literal, a sequence of bytes enclosed in double quotes.

Because Go source files are always encoded in UTF-8 and go text strings are conventionally interpreted as UTF-8, we can include Unicode code points in string literals.

Within a double-quoted string literal, escape sequences that begin with a backslash \ can be used to insert arbitrary byte values into the string.

Arbitrary bytes can also be include in literal strings using hexadecimal and octal escapes.

A raw string literal is written ..., using backquotes instead of double quotes. Within a raw string literal, no escape sequences are processed. Raw string literals are convenient ways to write regular expressions, which tend to have lots of backslashes.


Unicode collects all of the characters in all of the world’s writing systems, and assigns each one a standard number called Unicode code point, in Go terminology, a rune.

The natural data type to hold a single rune is int32, and that’s what Go uses; it has the synonym rune for precisely this purpose.


We could represent a sequence of runes as a sequence of int32 values. In this representation, which is called UTF-32 or UCS-4, the encoding of each Unicode code point has the same size, 32 bits. This is simple and uniform, but it uses much more space than necessary since most computer-readable text is in ASCII, which requires only 8 bits or 1 byte per character. All the characters in widespread use still number fewer that 65536, which would fit in 16 bits.

UTF-8 is a variable-length encoding of Unicode code points as bytes. It uses between 1 and 4 bytes to represent each rune, but only 1 byte for ASCII characters, and only 2 to 3 bytes for most runes in common use. The high-order bits of the first byte of the encoding for a rune indicate how many bytes follow. A high-order 0 indicates 7-bits ASCII, where each runes takes only 1 byte, so it identical to conventional ASCII. A high-order 110 indicates that the rune takes 2 bytes; the second byte begins with 10. Larger runes have analogous encodings.

A variable-length encoding precludes direct indexing to access the n-th character of a string, but UTF-8 has many desirable properties to compensate. The encoding is compact, compatible with ASCII, and self-synchronizing.

Unicode escape in Go string literals allow us to specify them by their numeric code point value. There are two forms, \uhhhh for a 16-bit value and \Uhhhhhhhh for a 32-bit value, where each h is a hexadecimal digit; the need for the 32-bit form arises very infrequently.

A rune whose value is less that 256 may be written with a single hexadecimal escape, such as \x41 for 'A', but for higher values, a \u or \U escape must be used. Consequently, \xe4\xb8\x96 is not a legal rune literal, even though those three bytes are a valid UTF-8 encoding of a single code point.

Thanks to the nice properties of UTF-8, many string operations don’t require decoding. We can test whether one string contains another as a prefix, or as a suffix, or as a substring using the same logic for UTF-8-encoded text as for raw bytes. This is not true for other encodings.

On the other hand, if we really care about the individual Unicode characters, we need a UTF-8 decoder. Go’s range loop, when applied to a string, performing UTF-8 decoding implicitly.

UTF-8 is exceptionally as an interchange format but within a program runes may be more convenient because they are of uniform size and are thus easily indexed in arrays and slices.

A []rune conversion applied to a UTF-8-encoded string return the sequence of Unicode code points that the string encodes. If a slice of runes is converted to a string, it produces the concatenation of the UTF-8 encodings of each rune.

Strings and Byte Slices

A strings contains an array of bytes that, once created, is immutable. By contrast, the elements of a byte slice can be modified freely.

Strings can be converted to byte slices and back again:

s := "abc"
b := []byte(s)
s2 := string(b)

Conceptually, the []byte(s) conversion allocates a new byte array holding a copy of the bytes of s, and yields a slice that references the entirety of that array. The conversion from byte slice back to string with string(b) also makes a copy, to ensure the immutability of the resulting string s2.

To avoid conversions and unnecessary memory allocation, many of the utility functions in the bytes packages directly parallel their counterparts in the string package.

The bytes package provides the Buffer type for efficient manipulation of byte slices. A Buffer starts out empty but grows as data of types like string, byte and byte[] are written to it. A bytes.Buffer variable requires no initialization because its zero value is usable.

When appending the UTF-8 encoding of a arbitrary rune to a bytes.Buffer, it’s best to use bytes.Buffer’s WriteRune method, but WriteByte is fine for ASCII characters such as [ and ].


Constants are expressions whose value is known to compiler and whose evaluation is guaranteed to occur at compile time, not at run time. The underlying type of every constant is a basic type: boolean, string, or number.

The results of all arithmetic, logic, and comparison operations applied to constant operands are themselves constants, as are the results of conversions and calls to certain built-in functions such as len, cap, real, imag, complex, and unsafe.Sizeof.

A constant declaration may specify a type as well as a value, but in the absence of an explicit type, the type is inferred from the expression on the right-hand side. In the following, time.Duration is a named type whose underlying type is int64, and time.Minute is a constant of that type. Both of the constants declared below thus have the type time.Duration as well, as revealed by %T:

const noDelay time.Duration = 0
const timeout = 5 * time.Minute
fmt.Printf("%T [1]%v\n", noDelay)       // "time.Duration 0"
fmt.Printf("%T [1]%v\n", timeout)       // "time.Duration 5m0s"
fmt.Printf("%T [1]%v\n", time.Minute)   // "time.Duration 1m0s"

Chapter 4: Composite Types

In this chapter, we’ll take a look at composite types, the molecules created by combining the basic types in various ways. We’ll talk about four such types–arrays, slices, maps, and structs.

Arrays and structs are aggregate types; their values are concatenations of other values in memory. Arrays are homogenous–their elements all have the same type–whereas structs are heterogenous. Both arrays and structs are fixed size. In contrast, slices and maps are dynamic data structures that grow as values are added.


An array is a fixed-length sequence of zero or more elements of a particular type. Because of their fixed length, arrays are rarely used directly in Go. Slices, which can grow and shrink, are much more versatile.

By default, the elements of a new array variable are initially set to zero value for the element type, which is 0 for numbers. We can use an array literal to initialize an array with a list of values.

var q [3]int = [3]int{1, 2, 3}
var r [3]int = [3]int{1, 2}

In an array literal, if an ellipsis “...” appears in place of the length, the array length is determined by the number of initializers. The definition of q can be simplified to:

q := [...]int{1, 2, 3}

The size of an array is part of its type, so [3]int and [4]int are different types. The size must be a constant expression, that is, an expression whose value can be computed as the program is being compiled.

If an array’s elements are comparable then the array type is comparable too, so we may directly compare two arrays of that type using the == operator, which reports whether all corresponding elements are equal. The != operator is its negation.

When a function is called, a copy of each argument value is assigned to the corresponding parameter variable, so the function receives a copy, not the original. Passing large arrays in this way can be inefficient, and any changes that the function makes to array elements affect only the copy, not the original. In this regard, Go treats arrays like any other type, but this behavior is different from languages that implicitly pass arrays by reference.

Of course, we can explicitly pass a pointer to an array so that any modifications the function make to array elements will be visible to the caller. This function zeroes the contents of a [32]byte array:

func zero(ptr * [32]byte) {
    *ptr = [32]byte{}

The array literal [32]byte{} yields an array of 32 bytes. Each element of the array has the zero value for byte, which is 0.

Using a pointer to an array is efficient and allow the called function to mutate the caller’s variable, but arrays are still inherently inflexible because of their fixed size. The zero function will not accept a pointer to a [16]byte variable, for example, nor is there any way to add or remove array elements. For those reasons, other than special cases like SHA256’s fix-sized hash, arrays are seldom used as functions parameters; instead, we use slices.


Slices represent variable-length sequences whose values all have the same type. A slice type is written []T, where the elements have type T; it looks like an array type without a size.

Arrays and slices are intimately connected. A slice is a lightweight data structure that gives access to a subsequence (or perhaps all) of the elements of an array, which is known as the slice’s underlying array. A slice has three components: a pointer, a length, and a capacity. The pointer points to the first element of the array that is reachable through the slice, which is not necessarily the array’s first element. The length is the number of slice elements; it can’t exceed the capacity, which is usually the number of elements between the start of the slice and the end of the underlying array. The built-in function s len and cap return those values.

Multiple slices can share the same underlying array and may refer to overlapping part of that array.

The slice operator s[i:j], where 0 <= i <= j <= cap(s), creates a new slice that refers to elements i through j-1 of the sequence s, which may be an array variable, a pointer to an array, or another slice.

Slicing beyond cap(s) causes a panic, but slicing beyond len(s) extends the slice, so the result may be longer than the original.

Since a slice contains a pointer to an element of an array, passing a slice to a function permits the function to modify the underlying array elements. In other words, copying a slice creates an alias for the underlying array. The function reverse reverses the elements of an []int slice in place, and it may be applied to slices of any length:

func reverse(s []int) {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]

Here we reverse the whole array a:

a := [...]int{0, 1, 2, 3, 4, 5}
fmt.Println(a) // "[5 4 3 2 1]"

A slice literal looks like an array literal, a sequence of values separated by comma and surrounded by braces, but the size is not given. This implicitly creates an array variable of the right size and yields a slice that points to it. As with array literals, slice literals may specify the values in order, or give their indices explicitly, or use a mix of the two styles.

Unlike arrays, slices are not comparable, so we cannot use == to test whether two slices contain the same elements. The standard library provides the highly optimized bytes.Equal function for comparing two slices of bytes ([]byte), but for other types of slice, we must do the comparison ourselves.

The only legal slice comparison is against nil. The zero value of a slice type is nil. A nil slice has no underlying array. The nil slice has length and capacity zero, but there are also non-nil slices of length and capacity zero, such as []int{} or make([]int, 3)[3:]. As with any type that can have nil values, the nil value of a particular slice type can be written using a conversion expression such as []int(nil).

var s []int     // len(s) == 0, s == nil
s = nil         // len(s) == 0, s == nil
s = []int(nil)  // len(s) == 0, s == nil
s = []int{}     // len(s) == 0, s != nil

So, if you need to test whether a slice is empty, use len(s) == 0, not s == nil. Other than comparing equal to nil, a nil slice behaves like any other zero-length slice; reverse(nil) is perfectly safe, for example. Unless clearly documented to the contrary, Go functions should treat all zero-length slices the same way, whether nil or non-nil.

The built-in function make creates a slice of a specified element type, length, and capacity. The capacity argument may be omitted, in which case the capacity equals the length.

make([]T, len)
make([]T, len, cap) // same as make([]T, cap)[:len]

Under the hood, make creates an unnamed array variable and returns a slice of it; the array is accessible only through the returned slice. In the first form, the slice is a view of the entire array. In the second, the slice is a view of only the array’s first len elements, but its capacity includes the entire array. The additional elements are set aside for future growth.

The append function

The built-in append function appends items to slices. The append function is crucial to understand how slices work, so let’s take a look at what’s going on. Here’s a version of called appendInt that is specialized for []int slices:

func appendInt([]int x, y int) []int) {
    var z int[]
    zlen := len(x) + 1
    if zlen <= cap(x) {
        z = x[:zlen]
    } else {
        // There is insufficient space. Allocate a new array.
        // Grow by doubling, for amortized linear complexity.
        zcap := zlen
        if zcap < 2 * len(x) {
            zcap = 2 * len(x)
        z = make([]int, zlen, zcap)
        copy(z, x)
    z[len(x)] = y
    return z

The built-in function copy, which copies elements from one slice to another of the same type. The slices may refer to the same underlying array; they may even overlap. copy return the number of elements actually copied, which is the smaller of the two slice lengths, so there is no danger of running off the end or overwriting something out of range.

The built-in append function may use a more sophisticated growth strategy than appendInt’s simplistic one. Usually we don’t know whether a given call to append will cause a reallocation, so we can’t assume the original slice refers to the same array as the resulting slice, nor that it refers to a different one. Similarly, we must assume that operations on elements of the old slice will (or will not) be reflected in the new slice. As a result, it’s usual to assign the result of a call to append to the same slice variable whose value we passed to append:

runes = append(runes, r)

Updating the slice variable is required not just when calling append, but for any function that may change the length or capacity of a slice or make it refer to a different underlying array. To use slices correctly, it’s important to bear in mind that although the elements of the underlying array are indirect, the slice’s pointer, length, and capacity are not. To update them requires an assignment like the one above. In this respect, slices are not “pure” reference types but resemble an aggregate type such as this struct:

type IntSlice struct {
    ptr *int
    len, cap int

Our appendInt function adds a single element to a slice, but the built-in append lets us add more that one new element, or even a whole slice of them.

var x []int
x = append(x, 1)
x = append(x, 2, 3)
x = append(x, 4, 5, 6)
x = append(x, x...) // appends the slice x
fmt.Println(x) // "[1 2 3 4 5 6 1 2 3 4 5 6]"

With the small modification shown below, we can match the behavior of the built-in append. The ellipsis ... in the declaration of appendInt makes the function variadic: it accepts any number of final arguments. The corresponding ellipsis in the call above shows how to supply a list of arguments from a slice.

func appendInt([]int, y []int {
    var z int[]
    zlen := len(x) + len(y)
    // expands z to at least zlen


The hash table is one of the most ingenious and versatile of all data structures. It is an unordered collection of key/pairs in which all the keys are distinct, an the value associated with a given key can be retrieved, updated or removed using a constant number of key comparisons on the average, no matter how large the hash table.

In Go, a map is a reference to a hash table, and a map type is written map[K]V, where K and V are the types of its keys and values. All of the keys in a given map are of the same type, and all of the values are of the same type, but the keys need not to be of the same type as the values. The key type K must be comparable with ==, so that the map can test whether a given key is equal to one already within it.

The built-in function make can be used to create a map:

ages := make(map[string]int) // mapping from strings to ints

We can also use a map literal to create a new map populated with some initial key/value pairs:

ages := map[string]int{
    "alice":    31,
    "charlie":  34,

So the expression for a new empty map is map[string]int{}.

Map elements are accessed through the usual subscript notation:

ages["alice"] = 32

and removed with the built-in function delete:

delete(ages, "alice")

All these operations are safe even if the element isn’t in the map; a map lookup using a key that isn’t present return the zero value for its type, so, for instance, the following works even when “bob” is not yet a key in the map because the value of ages["bob"] will be 0:

ages["bob"] = ages["bob"] + 1 // happy birthday!

But a map element is not a variable, and we cannot take its address:

_ = &ages["bob"] // compile error: cannot take address of map element

One reason that we can’t take the address of a map element is that growing a map might cause rehashing of existing elements into new storage locations, thus potentially invalidating the address.

To enumerate all the key/values pairs in the map, we used a range-based for loop similar to those we saw for slices:

for name, age := range ages {
    fmt.Printf("%s\t%d\n", name, age)

The order of map iteration is unspecified, and different implementations might use a different hash function, leading to different ordering. In practice, the order is random, varying from on execution to the next. This is intentional; make the sequence vary help force programs to be robust across implementations. To enumerate the key/value pairs in order, we must sort the key explicitly, for instance, using the Strings function from the sort package if the keys are string. This is a common pattern:

import "sort"

var names string[]
for name := range ages {
    names = append(names, name)
for _, name := range names {
    fmt.Printf("%s\t%d\n", name, ages[name])

Since we know the final size of names from the outset, it is more efficient to allocate an array of the required size up front:

names := make([]string, 0, len(ages))

The zero value for a map type is nil, that is, a reference to no hash table at all.

Most operations on maps, including lookup, delete, len and range loops, are safe to perform on a nil map reference, since it behaves like an empty map. But storing to a nil map causes a panic:

ages["carol"] = 24 // panic: assignment to entry in nil map

You must allocate the map before you can store into it.

The second return value of subscripting a map is a boolean that reports whether the element was present:

if age, ok := ages["bob"]; if !ok { /* ... */ }

As with slices, maps cannot be compared to each other; the only legal comparison is with nil.

Go does not provide a set type, but since the keys of the map are distinct, a map can serve this purpose.

The value type of a map can itself be a composite type, such as a map or slice.


A struct is an aggregate type that groups zero or more named values of arbitrary types as a single entity. Each value is called a field.

type Employee struct {
    ID          int
    Name        string
    Address     string
    DoB         time.Time
    Position    string
    Salary      int
    ManagerID   int

var dilbert Employee

The individual fields of a struct variable is accessed using dot notation like dilbert.Name, because dilbert is a variable, its fields are variables too, so we may assign to a field:

dilbert.Salary -= 500

or take its address and access it through a pointer:

position = &dilbert.Position
*position = 'Senior' + *position

The dot notation also works with a pointer to a struct:

var employeeOfMonth *Employee = &dilbert
employeeOfMonth.Position += " (proactive team player)"

The last statement is equivalent to:

(*employeeOfMonth).Position += " (proactive team player)"

Given an employee’s unique ID, the function EmployeeByID returns a pointer to an Employee struct. We can use dot notation to access its fields:

func EmployeeByID(id int) *Employess {/* ... */}

id := dilbert.ID
EmployeeByID(id).Salary = 0 // fired for... no real reason

The last statement updates the Employee struct that is pointed by the result of the call to EmployeeByID. If the result type of EmployeeByID were changed to Employee instead of *Employee, the assignment statement would not compile since its left-hand side would not identify a variable.

Field order is significant to type identity. Had we also combined the declaration of the Position filed(also a string), or interchanged Nmme and Address, we would be defining a different struct type. Typically we only combine the declarations of related fields.

The name of a struct field is exported if it begins with a capital letter; This is Go’s access mechanism. A struct type may contain a mixture of exported and unexported fields.

A named struct S can’t declare a field of the same type S: an aggregate value cannot contain itself. (An analogous restriction applies to arrays.) But S may declare a field of the pointer type *S, which let us create recursive data structures like linked lists and trees.

The zero value for a struct is composed of the zero values of each of its fields. It is usually desirable that the zero value be a natural or sensible default. For example, in bytes.Buffer, the initial value of the struct is a ready-to-use empty buffer, and the zero value of sync.Mutex is a ready-to-use unlocked mutex. Sometimes the sensible initial behavior happens for free, but sometimes the type designer has to work at it.

The struct type with no fields is called the empty struct, written struct{}. It has size zero and carries no information.

If all the fields of a struct are comparable, the struct itself is comparable, so two expressions of that type may be compared using == or !=. The == operation compares the corresponding fields of the two structs in order.

Struct Literals

A value of struct type can be written using a struct literal that specifies values for its fields:

type Point struct{ X, Y int }
p := Point{1, 2}

There are two forms of struct literal. The first form, shown above, requires that a value be specified for every field, in the right order. Accordingly, this form tends to be used only within the package that defines the struct type, or with smaller struct types for which there is an obvious field ordering convention, like image.Point{x, y} or color.RGBA{red, green, yello, alpha}. More often, the second form is used, in which a struct value is initialized by listing some or all of the field names and their corresponding values:

anim := gif.GIF{LoopCount: nframes}

If a field is omitted in this kind of literal, it is set to the zero value for its type. Because names are provided, the order of fields doesn’t matter.

The two forms cannot be mixed in the same literal. Nor can you use the (order-based) first form of literal to sneak around the rule that unexported identifiers may not be refered to from another package:

package p
type T struct{ a, b int }

package q
import "p"
var _ = p.T{a: 1, b: 2} // compile error
var _ = p.T{1, 2} // compile error

Because structs are so commonly dealt with through pointers, it’s possible to use this shorthand notation to create and initialize a struct variable and obtain its address:

pp := &Point{1, 2}

It is exactly equivalent to:

pp := new (Point)
*pp = Point{1, 2}

but &Point{1, 2} can be directly used within an expression, such as a function call.

Struct Embedding and Anonymous Fields

Go’s unusual struct embedding mechanism lets us use one named struct type as an anonymous field of another struct type, providing a convenient syntactic shortcut so that a simple dot expression like x.f can stand for a chain of fields like x.d.e.f.

type Point struct {
    X, Y int

type Circle struct {
    Radius int

type Wheel struct {
    Spokes int

Go lets us declare a field with a type but no name; such fields are called anonymous fields. The type of the field must be a named type or pointer to a named type. Thanks to embedding, we can refer to the names at the leaves of the implicit tree without giving the intervening names:

var w Wheel
w.X = 8 // equivalent to w.Circle.Point.X = 8

In fact, “anonymous” fields do have implicit names– the type names, so that you can’t have two anonymous fields of the same type since their names would conflict. And because the name of the field is implicitly determined by its type, so too is the visibility of the field. In the example above, the Point and Circle anonymous fields are exported. Had they been unexported(point and circle), we could still use the shorthand form:

w.X = 8 // equivalent to = 8

but the explicit long form shown in the comment would be forbidden outside the declaring package because circle and point would be inaccessible.

The shorthand notation used for selecting fields of an embedded type works for selecting its methods as well. In effect, the outer struct type gains not the fields of the embedded type but its methods too. This mechanism is the main way that complex object behaviors are composed from simpler ones. Composition is central to object-oriented programming in Go, and we’ll explore it later.

Chapter 6: Methods

Although there is no universally accepted definition of object-oriented programming, for our purposes, an object is simply a value or variable that has methods, and a method is a function associated with a particular type. An object-oriented program is one that uses methods to express properties and operations of each data structure so that clients need not access the object’s representation directly.

In allowing methods to be associated with any type, Go is unlike many other object-oriented languages. It is often convenient to define additional behaviors for simple types such as numbers, strings, slices, maps and sometimes functions. Methods may be declared on any named type defined in the same package, so long as its underlying type is neither a pointer nor an interface.

Method Declarations

A method is declared with a variant of the ordinary function declaration in which an extra parameter appear before the function name. The parameter attaches the function to the type of that parameter.

func (p Point) Distance (q Point) float64 {
    return math.Hypot(q.X-p.X, q.Y-p.Y)

The extra parameter p is called the method’s receiver, a legary from early object-oriented languages that described calling a method as “sending a message to an object.”

In Go, we don’t use a special name like this or self for the receiver; we choose receiver names just as we would for any other parameter. Since the receiver name will be frequently used, it’s a good idea to choose something short and to be consistent across methods. A common choice is the first letter of the type name, like p for Point.

Methods with a Pointer Receiver

Because calling a function makes a copy of each argument value, if a function need to update a variable, or if an argument is so large that we wish to avoid copying it, we must pass the address of the variable using a pointer. The same goes for methods that need to update the receiver variable: we attach them to the pointer type, such as *Point.

func (p *Point) ScaleBy(factor float64) {
    p.X *= factor
    p.Y *= factor

The name of this method is (*Point).ScaleBy. The parentheses are necessary; without them, the expression would be parse as *(Point.ScaleBy).

In a realistic program, convention dictates that if any method of Point has a pointer receiver, then all methods of Point should have a pointer receiver, even ones that don’t strictly need it.

Named types (Point) and pointers to them (*Point) are the only types that may appear in a receiver declaration. Furthermore, to avoid ambiguities, method declarations are not permitted on named types that are themselves pointer types:

type P *int
func (P) f() {...} // compile error: invalid receiver type

The (*Point).ScaleBy method can be called by providing a *Point receiver, like this:

r := &Point{1, 2}
fmt.Println(*r) // "{2, 4}"

or this:

p := Point{1, 2}
pptr := &p
fmt.Println(p) // "{2, 4}"

or this:

p: = Point{1, 2}
fmt.Println(p) // "{2, 4}"

But the last two cases are ungainly. Fortunately, the language helps us here. If the receiver p is a variable of type Point but the method requires a *Point receiver, we can use this shorthand:


and the compiler will perform an implicit &p on the variable. This only works for variables, including struct fields like p.X and array or slice elements like perim[0]. We cannot call a *Point method on a non-addressable Point receiver, because there’s no way to obtain the address of a temporary value:

Point{1, 2}.ScaleBy(2) // compile error: can't take address of Point literal

But we can call a Point method like Point.Distance with a *Point receiver, because there is a way to obtain the value from the address: just load the value pointed to by the receiver. The compiler inserts an implicit * operation for us. These two function calls are equivalent:


Let’s summarize these three case again, since they are a frequence point of confusion. In every valid method call expression, exactly one of these thress statements is true.

Either the receiver argument has the same type as receiver parameter, for example both have type T or both have type *T:

Point{1, 2}.Distance(q) // Point
pptr.ScaleBy(2) // *Point

Or the receiver argument is a variable of type T and the receiver parameter has type *T. The compiler implictly takes the address of the variable:

p.ScaleBy(2) // implicit (&p)

Or the receiver argument has type *T and the receiver parameter has type T. The compiler implictly dereferences the receiver, in other words, loads the value:

pptr.Distance(q) // implicit (*pptr)

If all the methods of a named type T have a receiver type of T itself (not *T), it is safe to copy instances of that type; calling any of its methods necessarily makes a copy. For example, time.Duration values are liberally copied, including as arguments to functions. But if any method has a pointer receiver, you should avoid copying instances of T because doing so may violate internal invariants. For example, copying an instance of bytes.Buffer would cause the original and the copy to alias the same underlying array of bytes. Subsequent method calls would have unpredictable effects.

Composing Types by Struct Embedding

import "image/color"

type Point struct{ X, Y float64 }

type ColoredPoint struct {
    Color color.RGBA

We can call methods of the embedded Point field using a receiver of type ColoredPoint, even though ColoredPoint has no declared methods:

red := color.RGBA{255, 0, 0, 255}
blue := color.RGBA{0, 0, 255, 255}
var p = ColoredPoint{Point{1, 1}, red}
var q = ColoredPoint{Point{5, 4}, blue}
fmt.Println(p.Distance(q.Point)) // 5
fmt.Println(p.Distance(q.Point)) // 10

The methods of Point have been promoted to ColoredPoint. In this way, embedding allows complex types with many methods to be built up by the composition of several fields, each providing a few methods.

Readers familar with class-based object-oriented languages may be tempted to view Point as a base class and ColoredPoint as a subclass or derived class, or to interpret the relationship between these types as if a ColoredPoint “is a” Point. But that would be a mistake. Notice the calls to Distance above. Distance has a parameter of type Point, and q is not a Point, so although q does have an embedded field of that type, we must explicitly select it. Attempting to pass q would be an error:

p.Distance(q) // compile error: cannot use q (ColoredPoint) as Point

A ColoredPoint is not a Point, but it “has a” Point, and it has two additional methods Distance and ScaleBy promoted from Point. If you prefer to think in terms of implementation, the emebedded field instructs the compiler to generate additional wrapper methods that delegate to the declared methods, equivalent to these:

func (p ColoredPoint) Distance(q point) float64 {
    return p.Point.Distance(q)

func (p *ColoredPoint) ScaleBy(factor float64) {

When Point.Distance is called its receiver value is p.Point, not p, and there is no way for the method to access the ColoredPoint in which the Point is embedded.

The type of an anonymous field may be a pointer to a named type, in which case fields and methods are promoted indirectly from the pointed-to object. Adding another level of indirection lets us share common structures and vary the relationships between objects dynamically. The declaration of ColoredPoint below embeds a *Point:

type ColoredPoint struct {
    Color color.RGBA

p := ColoredPoint{&Point{1, 1}, red}
q := ColoredPoint{&Point{5, 4}, blue}
fmt.Println(p.Distance(*q.Point)) // "5"
q.Point = q.Point
fmt.Println(*p.Point, *q.Point) // "{2 2} {2 2}"

A struct type may have more that one anonymous field. Had we declared ColoredPoint as

type ColoredPoint struct {

then a value of this type would have all the methods of Point, all the methods of RGBA, and any additional methods declared on ColoredPoint directly. When the compiler resolves a selector such as p.ScaleBy to a method, it first looks for a directly declared method named ScaleBy, then for methods promoted once from ColoredPoint’s embedded fields, then for methods promoted twice from embedded fields within Point and RGBA, and so on. The compiler reports an error if the selector was ambiguous because two methods we promoted from the same rank.

Methods can be declared only on named types (like Point) and pointers to them (*Point), but thanks to embedding, it’s possible and sometimes useful for unnamed struct type to have methods too:

var cache = struct {
    mapping map[string]string
} {
    mappding: make(map[string]string)

func lookup(key string) string {
    v := cache.mappding[key]
    return v

Chapter 7: Interfaces

Interface types express generalizations or abstractions about the behaviors of other types. By generalizing, interfaces let us write functions that are more flexible and adaptable because they are not tied to the details of one particular implementation.

Many object-oriented languages have some notion of interfaces, but what makes Go’s interfaces so distinctive is that they are satisfied implicitly. In other words, there is no need to declare all the interfaces that a concrete type satifies, simply possessing the necessay method is enough. This design lets you create new interface that are satisfied by existing concrete types without changing the existing types, which is particular useful for types defined in packages that you don’t control.

Interface as Contracts

All the types we’ve looked at so far have been concrete types. A concrete type specifies the exact representation of its values and expose the intrinsic operations of the representation, such as arithmetic for numbers, or indexing, append, and range for slices. A concrete type may also provide additional behaviors through its methods. When you have a value of a concrete type, you know exactly what it is and what you can do with it.

There is another kind of type in Go called an interface type. An interface is an abstract type. It doesn’t expose the representation or internal structure of its values, or the set of basic operations they support; it reveals only some of its methods. When you have a value of an interface type, you know nothing about what it is; you know only what it can do, or more precisely, what behaviors are provided by its methods.

package io

type Writer interface {
    Write(p []byte) (n int, err error)

The io.Writer interface defines the contract between Fprintf and its callers. On the one hand, the contract requires that the caller provide a value of a concrete type like *os.File or *bytes.Buffer that has a method called Write with the appropriate signature and behavior. On the other hand, the contract guarantees that Fprintf will do its job given any value that satisfies the io.Writer interface. Fprintf may not assume that it is writing to a file or to memory, only that it can call Write.

Because fmt.Printf assumes nothing about the representation of the value and relies only on the behaviors guaranteed by the io.Writer contract, we can safely pass a value of any concrete type that satisfies io.Writer as the first argument to fmt.Printf. This freedom to substitute one type for another that satisfies the same interface is called substitutability, and is a hallmark of object-oriented programming.

Interface Types

An interface type specifies a set of methods that a concrete type must possess to be considered an instance of that interface.