Go Bootcamp
Go Bootcamp
Go Bootcamp
Go Bootcamp
Everything you need to know to get started with Go.
Matt Aimonetti
ii
Contents
Preface
1 Intro
1.1 Objectives . . . . .
1.1.1 Knowledge
1.1.2 Skills . . .
1.1.3 Attitudes .
.
.
.
.
1
2
2
2
3
.
.
.
.
.
.
.
.
.
5
5
7
8
9
10
11
12
14
15
.
.
.
.
.
17
17
18
19
21
23
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2 The Basics
2.1 Variables & inferred typing . . . . . . . . . . . .
2.2 Constants . . . . . . . . . . . . . . . . . . . . .
2.3 Printing Constants and Variables . . . . . . . . .
2.4 Packages and imports . . . . . . . . . . . . . . .
2.5 Code location . . . . . . . . . . . . . . . . . . .
2.6 Exported names . . . . . . . . . . . . . . . . . .
2.7 Functions, signature, return values, named results
2.8 Pointers . . . . . . . . . . . . . . . . . . . . . .
2.9 Mutability . . . . . . . . . . . . . . . . . . . . .
3 Types
3.1 Basic types . .
3.2 Type conversion
3.3 Type assertion .
3.4 Structs . . . . .
3.5 Initializing . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
iii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
iv
3.6
3.7
Composition vs inheritance . . . . . . . . . . . . . . . . . . .
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.7.1 Solution . . . . . . . . . . . . . . . . . . . . . . . . .
4 Collection Types
4.1 Arrays . . . . . . . . . . . . . .
4.1.1 Printing arrays . . . . .
4.1.2 Multi-dimensional arrays
4.2 Slices . . . . . . . . . . . . . .
4.2.1 Slicing a slice . . . . . .
4.2.2 Making slices . . . . . .
4.2.3 Appending to a slice . .
4.2.4 Length . . . . . . . . .
4.2.5 Nil slices . . . . . . . .
4.2.6 Resources . . . . . . . .
4.3 Range . . . . . . . . . . . . . .
4.3.1 Break & continue . . . .
4.3.2 Range and maps . . . .
4.3.3 Exercise . . . . . . . . .
4.3.4 Solution . . . . . . . . .
4.4 Maps . . . . . . . . . . . . . .
4.4.1 Mutating maps . . . . .
4.4.2 Resources . . . . . . . .
4.4.3 Exercise . . . . . . . . .
4.4.4 Solution . . . . . . . . .
5 Control flow
5.1 If statement . . . . .
5.2 For Loop . . . . . .
5.3 Switch case statement
5.4 Exercise . . . . . . .
5.5 Solution . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
24
29
30
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
33
33
34
35
36
37
38
38
40
40
41
41
43
44
44
45
46
47
48
48
49
.
.
.
.
.
51
51
52
53
57
58
CONTENTS
6 Methods
6.1 Code organization . . . . . . . . . . . . . . . . . . . . . . . .
6.2 Type aliasing . . . . . . . . . . . . . . . . . . . . . . . . . .
6.3 Method receivers . . . . . . . . . . . . . . . . . . . . . . . .
59
60
61
62
7 Interfaces
7.1 Interfaces are satisfied implicitly
7.2 Errors . . . . . . . . . . . . . .
7.3 Exercise: Errors . . . . . . . . .
7.3.1 Solution . . . . . . . . .
.
.
.
.
65
67
68
69
69
.
.
.
.
.
.
.
.
.
71
72
73
74
75
77
77
78
79
81
.
.
.
.
.
.
85
85
85
86
86
86
87
.
.
.
.
8 Concurrency
8.1 Goroutines . . . . . . . . . . . . .
8.2 Channels . . . . . . . . . . . . .
8.2.1 Buffered channels . . . .
8.3 Range and close . . . . . . . . . .
8.4 Select . . . . . . . . . . . . . . .
8.4.1 Default case . . . . . . . .
8.4.2 Timeout . . . . . . . . . .
8.5 Exercise: Equivalent Binary Trees
8.5.1 Solution . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
9 Get Setup
9.1 OS X . . . . . . . . . . . . . . . .
9.1.1 Setup your paths . . . . . .
9.1.2 Install mercurial and bazaar
9.2 Windows . . . . . . . . . . . . . .
9.3 Linux . . . . . . . . . . . . . . . .
9.4 Extras . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
89
91
91
CONTENTS
vi
11.2 Alternate Ways to Import Packages . . . . . .
11.3 goimports . . . . . . . . . . . . . . . . . . .
11.4 Organization . . . . . . . . . . . . . . . . . .
11.5 Custom Constructors . . . . . . . . . . . . .
11.6 Breaking down code in packages . . . . . . .
11.7 Sets . . . . . . . . . . . . . . . . . . . . . .
11.8 Dependency package management . . . . . .
11.9 Using errors . . . . . . . . . . . . . . . . . .
11.10Quick look at some compilers optimizations
11.11Expvar . . . . . . . . . . . . . . . . . . . . .
11.12Set the build id using gits SHA . . . . . . .
11.13How to see what packages my app imports . .
11.14Web resources . . . . . . . . . . . . . . . . .
12 Exercises
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 92
. 92
. 92
. 93
. 93
. 94
. 96
. 96
. 97
. 99
. 99
. 100
. 101
103
Preface
Last updated: April 15, 2015
Cover art by Erick Zelaya sponsored by Ardan Studios
The Gopher character on the cover is based on the Go mascot designed by
Rene French and copyrighted under the Creative Commons Attribution 3.0
license.
This book is a companion book to the Go Bootcamp event organized for
the first time in Santa Monica CA in March 2014.
You dont need to install Go to read this book and follow along. This book
was designed to delay the setup process and let you evaluate Go without having
to go through the installation process. All examples and solutions are available
online in an environment allowing you to execute code right in the browser.
vii
viii
PREFACE
Chapter 1
Intro
The very first Go Bootcamp event was put together by Matt Aimonetti from
Splice with the support of a long list of volunteers.
Francesc Campoy
Mitchell Hashimoto
Evan Phoenix
Jeremy Saenz
Nic Williams
Ross Hale
Content and review has been provided by various members of the community, if you want to add content or provide corrections, feel free to send a pull
request to this books git repo.
Git hosting for this book is provided by GitHub and is available here.
This companion book contains material initially written specifically for this
event as well as content from Google & the Go team under Creative Commons
Attribution 3.0 License and code licensed under a BSD license. The rest of of
the content is also provided under Creative Commons Attribution 3.0 License.
1
CHAPTER 1. INTRO
1.1 Objectives
After the event, we expect the attendees to leave with the following knowledge,
skills and attributes.
1.1.1 Knowledge
pros/cons of static typing in Go
what makes Go unique
what is Go particularly good at
what are the challenging parts of Go
1.1.2 Skills
know how to do data modeling with Go
know how to organize code in packages
know how to test code
know how to write documentation
1.1. OBJECTIVES
know how to use JSON marshaling
know how to build a web API (depending on exercises)
know how to test a web API (depending on exercises)
know how to cross compile
know how to use the key go tools
1.1.3 Attitudes
value the potential of the Go language
can argue when to use Go vs using legacy language
consider using Go for a future project
CHAPTER 1. INTRO
Chapter 2
The Basics
Go is often referred to as a simple programming language, a language that
can be learned in a few hours if you already know another language. Go was
designed to feel familiar and to stay as simple as possible, the entire language
specification fits in just a few pages.
There are a few concepts we are going to explore before writing our first
application.
Or even
var (
name, location
age
string
int
6
Variables can also be declared one by one:
var name
string
var age
int
var location string
If an initializer is present, the type can be omitted, the variable will take the
type of the initializer (inferred typing).
var (
name
= "Prince Oberyn"
age
= 32
location = "Dorne"
)
2.2. CONSTANTS
See in Playground
A variable can contain any type, including functions:
func main() {
action := func() {
//doing something
}
action()
}
See in Playground
Outside a function, every construct begins with a keyword (var, func, and
so on) and the := construct is not available.
Gos declaration Syntax
2.2 Constants
Constants are declared like variables, but with the const keyword.
Constants can only be character, string, boolean, or numeric values and
cannot be declared using the := syntax. An untyped constant takes the type
needed by its context.
const Pi = 3.14
const (
StatusOK
StatusCreated
StatusAccepted
StatusNonAuthoritativeInfo
StatusNoContent
StatusResetContent
StatusPartialContent
)
package main
import "fmt"
=
=
=
=
=
=
=
200
201
202
203
204
205
206
8
const (
Pi
Truth
Big
Small
=
=
=
=
3.14
false
1 << 100
Big >> 99
)
func main() {
const Greeting = ""
fmt.Println(Greeting)
fmt.Println(Pi)
fmt.Println(Truth)
}
See in Playground
See in Playground
Read the fmt package documentation to see the available flags to create a
format specifier.
If you are writing an executable code (versus a library), then you need to
define a main package and a main() function which will be the entry point to
your software.
By convention, the package name is the same as the last element of the
import path. For instance, the math/rand package comprises files that begin
with the statement package rand.
Import statement examples:
import "fmt"
import "math/rand"
Or grouped:
import (
"fmt"
"math/rand"
)
Go Tour: Packages
Go Tour: Imports
Usually, non standard lib packages are namespaced using a web url. For
instance, I ported to Go some Rails logic, including the cryptography code
10
used in Rails 4. I hosted the source code containing a few packages on github,
in the following repository http://github.com/mattetti/goRailsYourself
To import the crypto package, I would need to use the following import
statement:
import "github.com/mattetti/goRailsYourself/crypto"
This command will pull down the code and put it in your Go path. When
installing Go, we set the GOPATH environment variable and that is whats used
to store binaries and libraries. Thats also where you should store your code
(your workspace).
$ ls $GOPATH
bin
pkg
src
The bin folder will contain the Go compiled binaries. You should probably
add the bin path to your system path.
The pkg folder contains the compiled versions of the available libraries so
the compiler can link against them without recompiling them.
Finally the src folder contains all the Go source code organized by import
path:
$ ls $GOPATH/src
bitbucket.org
code.google.com
11
github.com
$ ls $GOPATH/src/github.com/mattetti
goblin
goRailsYourself
launchpad.net
jet
and
func main() {
fmt.Println(math.Pi)
}
Pi is exported and can be accessed from outside the page, while pi isnt
available.
12
See in Playground
Use the provided Go documentation or godoc.org to find exported names.
Exported names example
See in Playground
In the following example, the location function returns two string values.
func location(city string) (string, string) {
var region string
var continent string
switch city {
case "Los Angeles", "LA",
region, continent
case "New York", "NYC":
region, continent
default:
region, continent
}
return region, continent
"Santa Monica":
= "California", "North America"
= "New York", "North America"
= "Unknown", "Unknown"
}
func main() {
region, continent := location("Santa Monica")
fmt.Printf("Matt lives in %s, %s", region, continent)
}
See in playground
Functions take parameters. In Go, functions can return multiple result parameters, not just a single value. They can be named and act just like variables.
If the result parameters are named, a return statement without arguments
returns the current values of the results.
14
See in Playground
I personally recommend against using named return parameters because
they often cause more confusion than they save time or help clarify your code.
2.8 Pointers
Go has pointers, but no pointer arithmetic. Struct fields can be accessed through
a struct pointer. The indirection through the pointer is transparent (you can
directly call fields and methods on a pointer).
Note that by default Go passes arguments by value (copying the arguments),
if you want to pass the arguments by reference, you need to pass pointers (or
use a structure using reference values like slices (Section 4.2) and maps (Section 4.4).
To get the pointer of a value, use the & symbol in front of the value, to
dereference a pointer, use the * symbol.
Methods are often defined on pointers and not values (although they can
be defined on both), so you will often store a pointer in a variable as in the
example below:
2.9. MUTABILITY
15
client := &http.Client{}
resp, err := client.Get("http://gobootcamp.com")
2.9 Mutability
In Go, only constants are immutable. However because arguments are passed
by value, a function receiving an value argument and mutating it, wont mutate
the original value.
package main
import "fmt"
type Artist struct {
Name, Genre string
Songs
int
}
func newRelease(a Artist) int {
a.Songs++
return a.Songs
}
func main() {
me := Artist{Name: "Matt", Genre: "Electro", Songs: 42}
fmt.Printf("%s released their %dth song\n", me.Name, newRelease(me))
fmt.Printf("%s has a total of %d songs", me.Name, me.Songs)
}
See in Playground
As you can see the total amount of songs on the me variables value wasnt
changed. To mutate the passed value, we need to pass it by reference, using a
pointer.
16
package main
import "fmt"
type Artist struct {
Name, Genre string
Songs
int
}
func newRelease(a *Artist) int {
a.Songs++
return a.Songs
}
func main() {
me := &Artist{Name: "Matt", Genre: "Electro", Songs: 42}
fmt.Printf("%s released their %dth song\n", me.Name, newRelease(me))
fmt.Printf("%s has a total of %d songs", me.Name, me.Songs)
}
See in Playground
The only change between the two versions is that newRelease takes a
pointer to an Artist value and when I initialize our me variable, I used the &
symbol to get a pointer to the value.
Another place where you need to be careful is when calling methods on
values as explained a bit later (Section 6.3)
Chapter 3
Types
3.1 Basic types
bool
string
Numeric types:
uint
int
uintptr
uint8
uint16
uint32
uint64
either 32 or 64 bits
same size as uint
an unsigned integer large enough to store the
a pointer value
the set of all unsigned 8-bit integers (0 to
the set of all unsigned 16-bit integers (0 to
the set of all unsigned 32-bit integers (0 to
the set of all unsigned 64-bit integers (0 to
uninterpreted bits of
255)
65535)
4294967295)
18446744073709551615)
int8
int16
int32
int64
float32
float64
complex64
complex128
the set of all complex numbers with float32 real and imaginary parts
the set of all complex numbers with float64 real and imaginary parts
byte
rune
17
CHAPTER 3. TYPES
18
Example of some of the built-in types:
package main
import (
"fmt"
"math/cmplx"
)
var (
goIsFun bool
= true
maxInt uint64
= 1<<64 - 1
complex complex128 = cmplx.Sqrt(-5 + 12i)
)
func main() {
const f = "%T(%v)\n"
fmt.Printf(f, goIsFun, goIsFun)
fmt.Printf(f, maxInt, maxInt)
fmt.Printf(f, complex, complex)
}
bool(true)
uint64(18446744073709551615)
complex128((2+3i))
See in Playground
19
i := 42
f := float64(i)
u := uint(f)
Go assignment between items of different type requires an explicit conversion which means that you manually need to convert types if you are passing a
variable to a function expecting another type.
See in playground
CHAPTER 3. TYPES
20
The type assertion doesnt have to be done on an empty interface. Its often
used when you have a function taking a param of a specific interface but the
function inner code behaves differently based on the actual object type. Here
is an example:
package main
import "fmt"
type Stringer interface {
String() string
}
type fakeString struct {
content string
}
// function used to implement the Stringer interface
func (s *fakeString) String() string {
return s.content
}
func printString(value interface{}) {
switch str := value.(type) {
case string:
fmt.Println(str)
case Stringer:
fmt.Println(str.String())
}
}
func main() {
s := &fakeString{"Ceci n'est pas un string"}
printString(s)
printString("Hello, Gophers")
}
See in Playground
Another example is when checking if an error is of a certain type:
if err != nil {
if msqlerr, ok := err.(*mysql.MySQLError); ok && msqlerr.Number == 1062 {
log.Println("We got a MySQL duplicate :(")
3.4. STRUCTS
21
} else {
return err
}
}
3.4 Structs
A struct is a collection of fields/properties. You can define new types as structs
or interfaces (Chapter 7). If you are coming from an object-oriented background, you can think of a struct to be a light class that supports composition
but not inheritance. Methods are discussed at length in Chapter 6
You dont need to define getters and setters on struct fields, they can be
accessed automatically. However, note that only exported fields (capitalized)
can be accessed from outside of a package.
A struct literal sets a newly allocated struct value by listing the values of its
fields. You can list just a subset of fields by using the "Name:" syntax (the
order of named fields is irrelevant when using this syntax). The special prefix
& constructs a pointer to a newly allocated struct.
package main
import (
"fmt"
"time"
)
type Bootcamp struct {
// Latitude of the event
Lat float64
// Longitude of the event
Lon float64
// Date of the event
Date time.Time
}
func main() {
fmt.Println(Bootcamp{
Lat: 34.012836,
CHAPTER 3. TYPES
22
Lon: -118.495338,
Date: time.Now(),
})
}
See in Playground
Declaration of struct literals:
package main
import "fmt"
type Point struct {
X, Y int
}
var (
p
q
r
s
Point{1, 2}
&Point{1, 2}
Point{X: 1}
Point{}
=
=
=
=
//
//
//
//
has
has
Y:0
X:0
type Point
type *Point
is implicit
and Y:0
)
func main() {
fmt.Println(p, q, r, s)
}
See in playground
Accessing fields using the dot notation:
package main
import (
"fmt"
"time"
)
type Bootcamp struct {
Lat, Lon float64
Date
time.Time
}
3.5. INITIALIZING
23
func main() {
event := Bootcamp{
Lat: 34.012836,
Lon: -118.495338,
}
event.Date = time.Now()
fmt.Printf("Event on %s, location (%f, %f)",
event.Date, event.Lat, event.Lon)
}
See in Playground
3.5 Initializing
Go supports the new expression to allocate a zeroed value of the requested type
and to return a pointer to it.
x := new(int)
CHAPTER 3. TYPES
24
func main() {
x := new(Bootcamp)
y := &Bootcamp{}
fmt.Println(*x == *y)
}
See in playground
Note that slices (Section 4.2), maps (Section 4.4) and channels (Section 8.2)
are usually allocated using make so the data structure these types are built upon
can be initialized.
Resources:
Allocation with new - effective Go
Composite Literals - effective Go
Allocation with make - effective Go
{
int
string
string
25
}
type Player struct {
User
GameId
}
int
func main() {
p := Player{}
p.Id = 42
p.Name = "Matt"
p.Location = "LA"
p.GameId = 90404
fmt.Printf("%+v", p)
}
See in Playground
The above example demonstrates a classic OOP challenge, our Player
struct has the same fields as the User struct but it also has a GameId field.
Having to duplicate the field names isnt a big deal, but it can be simplified by
composing our struct.
type User struct {
int
Id
Name, Location string
}
type Player struct {
User
GameId int
}
int
CHAPTER 3. TYPES
26
Name, Location string
}
type Player struct {
User
GameId int
}
func main() {
p := Player{}
p.Id = 42
p.Name = "Matt"
p.Location = "LA"
p.GameId = 90404
fmt.Printf("%+v", p)
}
See in Playground
The other option is to use a struct literal:
package main
import "fmt"
type User struct {
Id
int
Name, Location string
}
type Player struct {
User
GameId int
}
func main() {
p := Player{
User{Id: 42, Name: "Matt", Location: "LA"},
90404,
}
fmt.Printf(
"Id: %d, Name: %s, Location: %s, Game id: %d\n",
p.Id, p.Name, p.Location, p.GameId)
// Directly set a field define on the Player struct
p.Id = 11
fmt.Printf("%+v", p)
}
27
See in Playground
When using a struct literal with an implicit composition, we cant just pass
the composed fields. We instead need to pass the types composing the struct.
Once set, the fields are directly available.
Because our struct is composed of another struct, the methods on the User
struct is also available to the Player. Lets define a method to show that
behavior:
package main
import "fmt"
type User struct {
Id
int
Name, Location string
}
func (u *User) Greetings() string {
return fmt.Sprintf("Hi %s from %s",
u.Name, u.Location)
}
type Player struct {
User
GameId int
}
func main() {
p := Player{}
p.Id = 42
p.Name = "Matt"
p.Location = "LA"
fmt.Println(p.Greetings())
}
See in Playground
As you can see this is a very powerful way to build data structures but its
even more interesting when thinking about it in the context of interfaces. By
composing one of your structure with one implementing a given interface, your
structure automatically implements the interface.
CHAPTER 3. TYPES
28
See in playground
Our Job struct has a field called Logger which is a pointer to another type
(log.Logger)
When we initialize our value, we set the logger so we can then call its
Print function by chaining the calls: job.Logger.Print()
But Go lets you go even further and use implicit composition. We can skip
defining the field for our logger and now all the methods available on a pointer
to log.Logger are available from our struct:
package main
import (
"log"
"os"
)
type Job struct {
3.7. EXERCISE
29
Command string
*log.Logger
}
func main() {
job := &Job{"demo", log.New(os.Stderr, "Job: ", log.Ldate)}
job.Print("starting now...")
}
See in Playground
Note that you still need to set the logger and thats often a good reason to
use a constructor (custom constructor are used when you need to set a structure before using a value, see (Section 11.5) ). What is really nice with the
implicit composition is that it allows to easily and cheaply make your structs
implement interfaces. Imagine that you have a function that takes variables
implementing an interface with the Print method. My adding *log.Logger
to your struct (and initializing it properly), your struct is now implementing the
interface without you writing any custom methods.
3.7 Exercise
Looking at the User / Player example, you might have noticed that we composed Player using User but it might be better to compose it with a pointer
to a User struct. The reason why a pointer might be better is because in Go,
arguments are passed by value and not reference. If you have a small struct
that is inexpensive to copy, that is fine, but more than likely, in real life, our
User struct will be bigger and should not be copied. Instead we would want to
pass by reference (using a pointer). (Section 2.9 & Section 6.3 discuss more in
depth how calling a method on a type value vs a pointer affects mutability and
memory allocation)
Modify the code to use a pointer but still be able to initialize without using
the dot notation.
CHAPTER 3. TYPES
30
package main
import "fmt"
type User struct {
Id
int
Name, Location string
}
func (u *User) Greetings() string {
return fmt.Sprintf("Hi %s from %s",
u.Name, u.Location)
}
type Player struct {
*User
GameId int
}
func main() {
// insert code
}
See in Playground
Question: We defined the Greetings method on a pointer to a User type.
How come we were able to call it directly on the value?
3.7.1 Solution
package main
import "fmt"
type User struct {
Id
int
Name, Location string
}
func (u *User) Greetings() string {
return fmt.Sprintf("Hi %s from %s",
u.Name, u.Location)
}
type Player struct {
3.7. EXERCISE
31
*User
GameId int
}
func NewPlayer(id int, name, location string, gameId int) *Player {
return &Player{
User:
&User{id, name, location},
GameId: gameId,
}
}
func main() {
p := NewPlayer(42, "Matt", "LA", 90404)
fmt.Println(p.Greetings())
}
See in Playground
Answer: That is because methods defined on a pointer are also automatically available on the value itself. The example didnt use a pointer on purpose,
so the dot notation was working right away. In the pointer solution, a zero value
player is composed of a nil pointer of type User and therefore, we cant call a
field on a nil pointer.
32
CHAPTER 3. TYPES
Chapter 4
Collection Types
4.1 Arrays
The type [n]T is an array of n values of type T.
The expression:
var a [10]int
34
You can also set the array entries as you declare the array:
package main
import "fmt"
func main() {
a := [2]string{"hello", "world!"}
fmt.Printf("%q", a)
}
See in Playground
Finally, you can use an ellipsis to use an implicit length when you pass the
values:
package main
import "fmt"
func main() {
a := [...]string{"hello", "world!"}
fmt.Printf("%q", a)
}
See in Playground
4.1. ARRAYS
35
func main() {
a := [2]string{"hello", "world!"}
fmt.Println(a)
// [hello world!]
fmt.Printf("%s\n", a)
// [hello world!]
fmt.Printf("%q\n", a)
// ["hello" "world!"]
}
See in Playground
See in Playground
Trying to access or set a value at an index that doesnt exist will prevent
your program from compiling, for instance, try to compile the following code:
package main
func main() {
var a [2]string
a[3] = "Hello"
}
36
You will see that the compiler will report the following error:
Invalid array index 3 (out of bounds for 2-element array)
Thats because our array is of length 2 meaning that the only 2 available
indexes are 0 and 1. Trying to access index 3 results in an error that tells us that
we are trying to access an index that is of bounds since our array only contains
2 elements and we are trying to access the 4th element of the array.
Slices, the type that we are going to see next is more often used, due to the
fact that we dont always know in advance the length of the array we need.
4.2 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 transformation matrices, most array programming in Go is done with slices rather
than simple arrays.
Slices hold references to an underlying array, and if you assign one slice
to another, both refer to the same array. 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 slice points to an array of values and also includes a length. Slices can
be resized since they are just a wrapper on top of another data structure.
[]T is a slice with elements of type T.
package main
import "fmt"
func main() {
p := []int{2, 3, 5, 7, 11, 13}
fmt.Println(p)
// [2 3 5 7 11 13]
}
Go tour page
4.2. SLICES
37
is empty and
s[lo:lo+1]
See in Playground
Go tour page
38
See in Playground
It works by allocating a zeroed array and returning a slice that refers to that
array.
4.2. SLICES
package main
import "fmt"
func main() {
cities := []string{}
cities = append(cities, "San Diego")
fmt.Println(cities)
// [San Diego]
}
See in Playground
You can append more than one entry to a slice:
package main
import "fmt"
func main() {
cities := []string{}
cities = append(cities, "San Diego", "Mountain View")
fmt.Printf("%q", cities)
// ["San Diego" "Mountain View"]
}
See in Playground
And you can also append a slice to another using an ellipsis:
package main
import "fmt"
func main() {
cities := []string{"San Diego", "Mountain View"}
otherCities := []string{"Santa Monica", "Venice"}
cities = append(cities, otherCities...)
fmt.Printf("%q", cities)
// ["San Diego" "Mountain View" "Santa Monica" "Venice"]
}
39
40
See in Playground
Note that the ellipsis is a built-in feature of the language that means that
the element is a collection. We cant append an element of type slice of strings
([]string) to a slice of strings, only strings can be appended. However, using
the ellipsis (...) after our slice, we indicate that we want to append each
element of our slice. Because we are appending strings from another slice, the
compiler will accept the operation since the types are matching.
You obviously cant append a slice of type []int to another slice of type
[]string.
4.2.4 Length
At any time, you can check the length of a slice by using len:
package main
import "fmt"
func main() {
cities := []string{
"Santa Monica",
"San Diego",
"San Francisco",
}
fmt.Println(len(cities))
// 3
countries := make([]string, 42)
fmt.Println(len(countries))
// 42
}
See in Playground
4.3. RANGE
41
package main
import "fmt"
func main() {
var z []int
fmt.Println(z, len(z), cap(z))
// [] 0 0
if z == nil {
fmt.Println("nil!")
}
// nil!
}
4.2.6 Resources
For more details about slices:
Go slices, usage and internals
Effective Go - slices
Append function documentation
Slice tricks
Effective Go - slices
Effective Go - two-dimensional slices
Go by example - slices
4.3 Range
The range form of the for loop iterates over a slice (Section 4.2) or a map
(Section 4.4). Being able to iterate over all the elements of a data structure is
very useful and range simplifies the iteration.
42
package main
import "fmt"
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}
=
=
=
=
=
=
=
=
1
2
4
8
16
32
64
128
Go tour page
You can skip the index or value by assigning to _. If you only want the
index, drop the , value entirely.
package main
import "fmt"
func main() {
pow := make([]int, 10)
for i := range pow {
pow[i] = 1 << uint(i)
}
for _, value := range pow {
fmt.Printf("%d\n", value)
}
}
Go tour page
4.3. RANGE
43
See in Playground
You can also skip an iteration by using continue:
package main
import "fmt"
func main() {
pow := make([]int, 10)
for i := range pow {
if i%2 == 0 {
continue
}
pow[i] = 1 << uint(i)
}
fmt.Println(pow)
// [0 2 0 8 0 32 0 128 0 512]
}
See in Playground
44
See in Playground
4.3.3 Exercise
Given a list of names, you need to organize each name within a slice based on
its length.
package main
var names = []string{"Katrina", "Evan", "Neil", "Adam", "Martin", "Matt",
"Emma", "Isabella", "Emily", "Madison",
"Ava", "Olivia", "Sophia", "Abigail",
"Elizabeth", "Chloe", "Samantha",
"Addison", "Natalie", "Mia", "Alexis"}
4.3. RANGE
45
func main() {
// insert your code here
}
See in Playground
After you implement your solution, you should get the following output
(slice of slice of strings):
[[] [] [Ava Mia] [Evan Neil Adam Matt Emma] [Emily Chloe]
[Martin Olivia Sophia Alexis] [Katrina Madison Abigail Addison Natalie]
[Isabella Samantha] [Elizabeth]]
4.3.4 Solution
package main
import "fmt"
var names = []string{"Katrina", "Evan", "Neil", "Adam", "Martin", "Matt",
"Emma", "Isabella", "Emily", "Madison",
"Ava", "Olivia", "Sophia", "Abigail",
"Elizabeth", "Chloe", "Samantha",
"Addison", "Natalie", "Mia", "Alexis"}
func main() {
var maxLen int
for _, name := range names {
if l := len(name); l > maxLen {
maxLen = l
}
}
output := make([][]string, maxLen)
for _, name := range names {
output[len(name)-1] = append(output[len(name)-1], name)
}
fmt.Printf("%v", output)
}
See in Playground
46
There are a few interesting things to note. To avoid an out of bounds insert,
we need our output slice to be big enough. But we dont want it to be too
big. Thats why we need to do a first pass through all the names and find the
longest. We use the longest name length to set the length of the output slice
length. Slices are zero indexed, so when inserting the names, we need to get
the length of the name minus one.
4.4 Maps
Maps are somewhat similar to what other languages call dictionaries or hashes.
A map maps keys to values. Here we are mapping string keys (actor names)
to an integer value (age).
package main
import "fmt"
func main() {
celebs := map[string]int{
"Nicolas Cage":
"Selena Gomez":
"Jude Law":
"Scarlett Johansson":
}
50,
21,
41,
29,
fmt.Printf("%#v", celebs)
}
See in Playground
When not using map literals like above, maps must be created with make
(not new) before use. The nil map is empty and cannot be assigned to.
Assignments follow the Go convention and can be observed in the example
below.
4.4. MAPS
47
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{40.68433, -74.39967}
fmt.Println(m["Bell Labs"])
}
See in Playground
When using map literals, if the top-level type is just a type name, you can
omit it from the elements of the literal.
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
// same as "Bell Labs": Vertex{40.68433, -74.39967}
"Google": {37.42202, -122.08408},
}
func main() {
fmt.Println(m)
}
See in Playground
48
m[key] = elem
Retrieve an element:
elem = m[key]
Delete an element:
delete(m, key)
See in Playground
If key is in m, ok is true. If not, ok is false and elem is the zero value for
the maps element type. Similarly, when reading from a map if the key is not
present the result is the zero value for the maps element type.
4.4.2 Resources
Go team blog post on maps
Effective Go - maps
4.4.3 Exercise
Implement WordCount.
4.4. MAPS
49
package main
import (
"code.google.com/p/go-tour/wc"
)
func WordCount(s string) map[string]int {
return map[string]int{"x": 1}
}
func main() {
wc.Test(WordCount)
}
See in Playground
Online assignment
It should return a map of the counts of each word in the string s. The
wc.Test function runs a test suite against the provided function and prints
success or failure.
You might find strings.Fields helpful.
4.4.4 Solution
package main
import (
"code.google.com/p/go-tour/wc"
"strings"
)
func WordCount(s string) map[string]int {
words := strings.Fields(s)
count := map[string]int{}
for _, word := range words {
count[word]++
}
return count
}
func main() {
wc.Test(WordCount)
}
50
See in Playground
Chapter 5
Control flow
5.1 If statement
The if statement looks as it does in C or Java, except that the ( ) are gone
and the { } are required. Like for, the if statement can start with a short
statement to execute before the condition. Variables declared by the statement
are only in scope until the end of the if. Variables declared inside an if short
statement are also available inside any of the else blocks.
If statement example
if answer != 42 {
return "Wrong answer"
}
52
Infinite loop
for {
// do something in a loop forever
}
53
See in Playground
There are a few interesting things to know about this statement in Go:
You can only compare values of the same type.
You can set an optional default statement to be executed if all the others
fail.
You can use an expression in the case statement, for instance you can
calculate a value to use in the case:
package main
import "fmt"
func main() {
num := 3
54
v := num % 2
switch v {
case 0:
fmt.Println("even")
case 3 - 2:
fmt.Println("odd")
}
}
See in Playground
You can have multiple values in a case statement:
package main
import "fmt"
func main() {
score := 7
switch score {
case 0, 1, 3:
fmt.Println("Terrible")
case 4, 5:
fmt.Println("Mediocre")
case 6, 7:
fmt.Println("Not bad")
case 8, 9:
fmt.Println("Almost perfect")
case 10:
fmt.Println("hmm did you cheat?")
default:
fmt.Println(score, " off the chart")
}
}
See in Playground
You can execute all the following statements after a match using the
fallthrough statement:
package main
import "fmt"
func main() {
n := 4
switch n {
case 0:
fmt.Println("is zero")
fallthrough
case 1:
fmt.Println("is <= 1")
fallthrough
case 2:
fmt.Println("is <= 2")
fallthrough
case 3:
fmt.Println("is <= 3")
fallthrough
case 4:
fmt.Println("is <= 4")
fallthrough
case 5:
fmt.Println("is <= 5")
fallthrough
case 6:
fmt.Println("is <= 6")
fallthrough
case 7:
fmt.Println("is <= 7")
fallthrough
case 8:
fmt.Println("is <= 8")
fallthrough
default:
fmt.Println("Try again!")
}
}
is <= 4
is <= 5
is <= 6
is <= 7
is <= 8
Try again!
See in Playground
55
56
You can use a break statement inside your matched statement to exit the
switch processing:
package main
import (
"fmt"
"time"
)
func main() {
n := 1
switch n {
case 0:
fmt.Println("is zero")
fallthrough
case 1:
fmt.Println("<= 1")
fallthrough
case 2:
fmt.Println("<= 2")
fallthrough
case 3:
fmt.Println("<= 3")
if time.Now().Unix()%2 == 0 {
fmt.Println("un pasito pa lante maria")
break
}
fallthrough
case 4:
fmt.Println("<= 4")
fallthrough
case 5:
fmt.Println("<= 5")
}
}
See in Playground
<=
<=
<=
un
1
2
3
pasito pa lante maria
5.4. EXERCISE
57
5.4 Exercise
You have 50 bitcoins to distribute to 10 users: Matthew, Sarah, Augustus,
Heidi, Emilie, Peter, Giana, Adriano, Aaron, Elizabeth The coins will be distributed based on the vowels contained in each name where:
a: 1 coin e: 1 coin i: 2 coins o: 3 coins u: 4 coins
and a user cant get more than 10 coins. Print a map with each users name
and the amount of coins distributed. After distributing all the coins, you should
have 2 coins left.
The output should look something like that:
map[Matthew:2 Peter:2 Giana:4 Adriano:7 Elizabeth:5 Sarah:2 Augustus:10 Heidi:5 Emilie:6 Aaron:5
Coins left: 2
Note that Go doesnt keep the order of the keys in a map, so your results
might not look exactly the same but the key/value mapping should be the same.
Here is some starting code:
package main
import "fmt"
var (
coins = 50
users = []string{
"Matthew", "Sarah", "Augustus", "Heidi", "Emilie",
"Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
}
distribution = make(map[string]int, len(users))
)
func main() {
fmt.Println(distribution)
fmt.Println("Coins left:", coins)
}
See in Playground
58
5.5 Solution
package main
import "fmt"
var (
coins = 50
users = []string{
"Matthew", "Sarah", "Augustus", "Heidi", "Emilie",
"Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
}
distribution = make(map[string]int, len(users))
)
func main() {
coinsForUser := func(name string) int {
var total int
for i := 0; i < len(name); i++ {
switch string(name[i]) {
case "a", "A":
total++
case "e", "E":
total++
case "i", "I":
total = total + 2
case "o", "O":
total = total + 3
case "u", "U":
total = total + 4
}
}
return total
}
for _, name := range users {
v := coinsForUser(name)
if v > 10 {
v = 10
}
distribution[name] = v
coins = coins - v
}
fmt.Println(distribution)
fmt.Println("Coins left:", coins)
}
See in Playground
Chapter 6
Methods
While technically Go isnt an Object Oriented Programming language, types
and methods allow for an object-oriented style of programming. The big difference is that Go does not support type inheritance but instead has a concept
of interface.
In this chapter, we will focus on Gos use of methods and interfaces.
Note: A frequently asked question is what is the difference between a
function and a method. A method is a function that has a defined receiver, in
OOP terms, a method is a function on an instance of an object.
Go does not have classes. However, you can define methods on struct types.
The method receiver appears in its own argument list between the func
keyword and the method name. Here is an example with a User struct containing two fields: FirstName and LastName of string type.
package main
import (
"fmt"
)
type User struct {
FirstName, LastName string
}
func (u User) Greeting() string {
return fmt.Sprintf("Dear %s %s", u.FirstName, u.LastName)
}
59
60
CHAPTER 6. METHODS
func main() {
u := User{"Matt", "Aimonetti"}
fmt.Println(u.Greeting())
}
See in playground
Note how methods are defined outside of the struct, if you have been writing
Object Oriented code for a while, you might find that a bit odd at first. The
method on the User type could be defined anywhere in the package.
61
}
// List of functions
func NewUser(firstName, lastName string) *User {
return &User{FirstName: firstName,
LastName: lastName,
Location: &UserLocation{
City:
"Santa Monica",
Country: "USA",
},
}
}
// List of methods
func (u *User) Greeting() string {
return fmt.Sprintf("Dear %s %s", u.FirstName, u.LastName)
}
In fact, you can define a method on any type you define in your package,
not just structs. You cannot define a method on a type from another package,
or on a basic type.
CHAPTER 6. METHODS
62
Playground Example
package main
import (
"fmt"
"math"
)
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
}
Playground Example
63
}
func (u *User) Greeting() string {
return fmt.Sprintf("Dear %s %s", u.FirstName, u.LastName)
}
func main() {
u := &User{"Matt", "Aimonetti"}
fmt.Println(u.Greeting())
}
See in playground
Remember that Go passes everything by value, meaning that when Greeting()
is defined on the value type, every time you call Greeting(), you are copying the User struct. Instead when using a pointer, only the pointer is copied
(cheap).
The other reason why you might want to use a pointer is so that the method
can modify the value that its receiver points to.
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := &Vertex{3, 4}
v.Scale(5)
fmt.Println(v, v.Abs())
}
64
CHAPTER 6. METHODS
See in playground
In the example above, Abs() could be defined on the value type or the
pointer since the method doesnt modify the receiver value (the vertex). However Scale() has to be defined on a pointer since it does modify the receiver.
Scale() resets the values of the X and Y fields.
Go tour page
Chapter 7
Interfaces
An interface type is defined by a set of methods. A value of interface type can
hold any value that implements those methods.
Here is a refactored version of our earlier example. This time we made
the greeting feature more generic by defining a function called Greet which
takes a param of interface type Namer. Namer is a new interface we defined
which only defines one method: Name(). So Greet() will accept as param
any value which has a Name() method defined.
To make our User struct implement the interface, we defined a Name()
method. We can now call Greet and pass our pointer to User type.
package main
import (
"fmt"
)
type User struct {
FirstName, LastName string
}
func (u *User) Name() string {
return fmt.Sprintf("%s %s", u.FirstName, u.LastName)
}
type Namer interface {
Name() string
}
65
CHAPTER 7. INTERFACES
66
func Greet(n Namer) string {
return fmt.Sprintf("Dear %s", n.Name())
}
func main() {
u := &User{"Matt", "Aimonetti"}
fmt.Println(Greet(u))
}
See in playground
We could now define a new type that would implement the same interface
and our Greet function would still work.
package main
import (
"fmt"
)
type User struct {
FirstName, LastName string
}
func (u *User) Name() string {
return fmt.Sprintf("%s %s", u.FirstName, u.LastName)
}
type Customer struct {
Id
int
FullName string
}
func (c *Customer) Name() string {
return c.FullName
}
type Namer interface {
Name() string
}
func Greet(n Namer) string {
return fmt.Sprintf("Dear %s", n.Name())
}
func main() {
u := &User{"Matt", "Aimonetti"}
fmt.Println(Greet(u))
c := &Customer{42, "Francesc"}
67
fmt.Println(Greet(c))
}
See in playground
CHAPTER 7. INTERFACES
68
See in playground
Package io defines Reader and Writer so you dont have to.
7.2 Errors
An error is anything that can describe itself as an error string. The idea is
captured by the predefined, built-in interface type, error, with its single method,
Error, returning a string:
type error interface {
Error() string
}
The fmt packages various print routines automatically know to call the
method when asked to print an error.
package main
import (
"fmt"
"time"
)
type MyError struct {
When time.Time
What string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}
func main() {
if err := run(); err != nil {
69
fmt.Println(err)
}
}
See in Playground
7.3.1 Solution
CHAPTER 7. INTERFACES
70
package main
import (
"fmt"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %g", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
z := 1.0
for i := 0; i < 10; i++ {
z = z - ((z*z)-x)/(2*z)
}
return z, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
See in playground
Tip: When doing an inferred declaration of a float, you can omit the decimal value and do the following:
z := 1.
// same as
// z := 1.0
Chapter 8
Concurrency
Concurrent programming is a large topic but its also one of the most interesting
aspects of the Go language.
Concurrent programming in many environments is made difficult by the
subtleties required to implement correct access to shared variables. Go encourages a different approach in which shared values are passed around on channels
and, in fact, never actively shared by separate threads of execution. Only one
goroutine has access to the value at any given time. Data races cannot occur,
by design. To encourage this way of thinking we have reduced it to a slogan:
Do not communicate by sharing memory; instead, share memory by communicating.
This approach can be taken too far. Reference counts may be best done
by putting a mutex around an integer variable, for instance. But as a high-level
approach, using channels to control access makes it easier to write clear, correct
programs.
Although Gos approach to concurrency originates in Hoares Communicating Sequential Processes (CSP), it can also be seen as a type-safe generalization
of Unix pipes.
Rob Pikes concurrency slides (IO 2012)
Video of Rob Pike at IO 2012
Video of Concurrency is not parallelism (Rob Pike)
71
CHAPTER 8. CONCURRENCY
72
8.1 Goroutines
A goroutine is a lightweight thread managed by the Go runtime.
go f(x, y, z)
See in playground
Go tour page
8.2. CHANNELS
73
8.2 Channels
Channels are a typed conduit through which you can send and receive values
with the channel operator, <-.
ch <- v
v := <-ch
By default, sends and receives block wait until the other side is ready. This
allows goroutines to synchronize without explicit locks or condition variables.
package main
import "fmt"
func sum(a []int, c
sum := 0
for _, v :=
sum
}
c <- sum //
}
chan int) {
range a {
+= v
send sum to c
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
See in playground
Go tour page
CHAPTER 8. CONCURRENCY
74
Sends to a buffered channel block only when the buffer is full. Receives
block when the buffer is empty.
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
See in playground
But if you do:
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 1
c <- 2
c <- 3
fmt.Println(<-c)
fmt.Println(<-c)
fmt.Println(<-c)
}
See in playground
You are getting a deadlock:
75
Thats because we overfilled the buffer without letting the code a chance to
read/remove a value from the channel.
However, this version using a goroutine would work fine:
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 1
c <- 2
c3 := func() { c <- 3 }
go c3()
fmt.Println(<-c)
fmt.Println(<-c)
fmt.Println(<-c)
}
See in playground
The reason is that we are adding an extra value from inside a go routine, so
our code doesnt block the main thread. The goroutine is being called before
the channel is being emptied, but that is fine, the goroutine will wait until the
channel is available. We then read a first value from the channel, which frees a
spot and our goroutine can push its value to the channel.
Go tour page
CHAPTER 8. CONCURRENCY
76
v, ok := <-ch
ok is false if there are no more values to receive and the channel is closed.
The loop for i := range ch receives values from the channel repeatedly until it is closed.
Note: Only the sender should close a channel, never the receiver. Sending
on a closed channel will cause a panic.
Another note: Channels arent like files; you dont usually need to close
them. Closing is only necessary when the receiver must be told there are no
more values coming, such as to terminate a range loop.
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
See in playground
Go tour page
8.4. SELECT
77
8.4 Select
The select statement lets a goroutine wait on multiple communication operations.
A select blocks until one of its cases can run, then it executes that case.
It chooses one at random if multiple are ready.
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
See in playground
CHAPTER 8. CONCURRENCY
78
select {
case i := <-c:
// use i
default:
// receiving from c would block
}
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
.")
fmt.Println("
time.Sleep(50 * time.Millisecond)
}
}
}
See in playground
Go tour page
8.4.2 Timeout
package main
import (
"fmt"
"log"
"net/http"
"time"
79
)
func main() {
response := make(chan *http.Response, 1)
errors := make(chan *error)
go func() {
resp, err := http.Get("http://matt.aimonetti.net/")
if err != nil {
errors <- &err
}
response <- resp
}()
for {
select {
case r := <-response:
fmt.Printf("%s", r.Body)
return
case err := <-errors:
log.Fatal(err)
case <-time.After(200 * time.Millisecond):
fmt.Printf("Timed out!")
return
}
}
}
See in playground but note that in playground, you wont get a response
due to sandboxing.
We are using the time.After call as a timeout measure to exit if the request didnt give a response within 200ms.
CHAPTER 8. CONCURRENCY
80
A function to check whether two binary trees store the same sequence is
quite complex in most languages. Well use Gos concurrency and channels to
write a simple solution.
This example uses the tree package, which defines the type:
type Tree
Left
Value
Right
}
struct {
*Tree
int
*Tree
Then read and print 10 values from the channel. It should be the numbers
1, 2, 3, . . . , 10.
81
8.5.1 Solution
If you print tree.New(1) you will see the following tree:
((((1 (2)) 3 (4)) 5 ((6) 7 ((8) 9))) 10)
CHAPTER 8. CONCURRENCY
82
close(ch)
}
// recWalk walks recursively through the tree and push values to the channel
// at each recursion
func recWalk(t *tree.Tree, ch chan int) {
if t != nil {
// send the left part of the tree to be iterated over first
recWalk(t.Left, ch)
// push the value to the channel
ch <- t.Value
// send the right part of the tree to be iterated over last
recWalk(t.Right, ch)
}
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
ch1 := make(chan int)
ch2 := make(chan int)
go Walk(t1, ch1)
go Walk(t2, ch2)
for {
x1, ok1 := <-ch1
x2, ok2 := <-ch2
switch {
case ok1 != ok2:
// not the same size
return false
case !ok1:
// both channels are empty
return true
case x1 != x2:
// elements are different
return false
default:
// keep iterating
}
}
}
func main() {
ch := make(chan int)
go Walk(tree.New(1), ch)
for v := range ch {
fmt.Println(v)
}
fmt.Println(Same(tree.New(1), tree.New(1)))
fmt.Println(Same(tree.New(1), tree.New(2)))
83
The comparison of the two trees is trivial once we know how to extract the
values of each tree. We just need to loop through the first tree (via the channel),
read the value, get the value from the second channel (walking the second tree)
and compare the two values.
See in playground
84
CHAPTER 8. CONCURRENCY
Chapter 9
Get Setup
9.1 OS X
The easiest way to install Go for development on OS X is to use homebrew.
Using Terminal.app install homebrew:
86
$ mkdir $HOME/go
$ export GOPATH=$HOME/go
Note that if we open a new tab or restart our machine, Go wont know where
to find our workspace. For that, you need to set the export in your profile:
$ open $HOME/.bash_profile
Add a new entry to set GOPATH and add the workspaces bin folder to your
system path:
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
This will allow your Go workspace to always be set and will allow you to
call the binaries you compiled.
Official resource
9.2 Windows
Install the latest version by downloading the latest installer.
Official resource
9.3 Linux
Install from one of the official linux packages Setup your path, as explained in
Section 9.1.1
9.4. EXTRAS
87
9.4 Extras
Installing Godoc, vet and Golint, three very useful Go tools from the Go
team, is highly recommended:
$ go get golang.org/x/tools/cmd/godoc
$ go get golang.org/x/tools/cmd/vet
$ go get github.com/golang/lint/golint
Official resource
88
Chapter 10
90
Chapter 11
92
Learn and become familiar with tools and utilities, or create your own!
They are as vital to your success as knowing the language. @coreyprak
11.3 goimports
Goimports is a tool that updates your Go import lines, adding missing ones and
removing unreferenced ones.
It acts the same as gofmt (drop-in replacement) but in addition to code
formatting, also fixes imports.
11.4 Organization
Go is a pretty easy programming language to learn but the hardest thing that
developers at first is how to organize their code. Rails became popular for
many reasons and scaffolding was one of them. It gave new developers clear
directions and places to put their code and idioms to follow.
93
To some extent, Go does the same thing by providing developers with great
tools like go fmt and by having a strict compiler that wont compile unused
variables or unused import statements.
package main
import (
"log"
"os"
)
type Job struct {
Command string
*log.Logger
}
func NewJob(command string) *Job {
return &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)}
}
func main() {
NewJob("demo").Print("starting now...")
}
94
11.7 Sets
You might want to find a way to extract unique value from a collection. In
other languages, you often have a set data structure not allowing duplicates. Go
doesnt have that built in, however its not too hard to implement (due to a lack
of generics, you do need to do that for most types, which can be cumbersome).
// UniqStr returns a copy if the passed slice with only unique string results.
func UniqStr(col []string) []string {
m := map[string]struct{}{}
for _, v := range col {
if _, ok := m[v]; !ok {
m[v] = struct{}{}
}
}
list := make([]string, len(m))
i := 0
for v := range m {
list[i] = v
i++
}
return list
}
See in playground
I used a few interesting tricks that are interesting to know. First, the map of
empty structs:
m := map[string]struct{}{}
We create a map with the keys being the values we want to be unique,
the associated value doesnt really matter much so it could be anything. For
instance:
m := map[string]bool{}
11.7. SETS
95
if _, ok := m[v]; !ok {
m[v] = struct{}{}
}
What we are doing here, is simply check if there is a value associated with
the key v in the map m, we dont care about the value itself, but if we know that
we dont have a value, then we add one.
Once we have a map with unique keys, we can extract them into a new slice
of strings and return the result.
Here is the test for this function, as you can see, I used a table test, which
is the idiomatic Go way to run unit tests:
96
97
See in Playground
Build your file (here called t.go) passing some gcflags:
98
The compiler notices that it can inline the NewUser function defined on
line 15 and inline it on line 21. Dave Cheney has a great post about why Gos
inlining is helping your programs run faster.
Basically, the compiler moves the body of the NewUser function (L15) to
where its being called (L21) and therefore avoiding the overhead of a function
call but increasing the binary size.
The compiler creates the equivalent of:
func main() {
id := 42 + 1
u := &User{id, "Matt", "LA"}
fmt.Println(u.Greetings())
}
On a few lines, you see the potentially alarming leaking param message.
It doesnt mean that there is a memory leak but that the param is kept alive even
after returning. The leaked params are:
On the Greetingss method: u (receiver)
On the NewUsers functon: name, location
The reason why u leaks in the Greetings method is because its being
used in the fmt.Sprintf function call as an argument. name and location
11.11. EXPVAR
99
are also leaked because they are used in the Users literal value. Note that
id doesnt leak because its a value, only references and pointers can leak.
X argument does not escape means that the argument doesnt escape the function, meaning that its not used outside of the function so its
safe to store it on the stack.
On the other hand, you can see that &User literal escapes to heap.
What it means is that the address of a literal value is used outside of the function and therefore cant be stored on the stack. The value could be stored on
the stack, except a pointer to the value escapes the function, so the value has to
be moved to the heap to prevent the pointer referring to incorrect memory once
the function returns. This is always the case when calling a method on a value
and the method uses one or more fields.
11.11 Expvar
TODO package
The next step is to set an exported variable that you will set at compilation
time using the -ldflags flag.
package main
import "fmt"
100
See in playground
Save the above code in a file called example.go. If you run the above
code, Build wont be set, for that you need to set it using go build and the
-ldflags.
$ go build -ldflags "-X main.Build a1064bc" example.go
Now, hook that into your deployment compilation process, I personally like
Rake to do that, and this way, every time I compile, I think of Jim Weirich.
101
$ cd $GOPATH/src/github.com/GoBootcamp/clirescue
$ go list -f '{{join .Deps "\n"}}' |
xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}'
github.com/GoBootcamp/clirescue/cmdutil
github.com/GoBootcamp/clirescue/trackerapi
github.com/GoBootcamp/clirescue/user
github.com/codegangsta/cli
If you want the list to also contain standard packages, edit the template and
use:
$ go list -f '{{join .Deps "\n"}}' |
102
Chapter 12
Exercises
We have 4 exercises to choose from, each exercise focuses on different challenges. Try to tackle the exercise of your choice by pairing with at least one
person, ideally try to have 4 people by group.
Fork the original repo (with the instructions), work on it and send a pull
request when you are done.
If your group wants to work on a new exercise, please create an assignment
and send it to me (Matt) so I can add it to the repository and the following list.
avatar me
Hashing
image manipulation
remote commands
Concurrency (channels, go routines)
Network interface
depending on the commands you implement
copernic 2000
concurrency
103
104