Learninggo 2
Learninggo 2
Gieben
Internet-Draft August 25, 2018
Intended status: Informational
Expires: February 26, 2019
Learning Go
draft-learning-go-00
Preface
The source of this book [1] is written in mmark [2] and is converted
from the original LaTeX source [3].
_All example code used in this book is hereby licensed under the
Apache License version 2.0._
"Learning Go" has been translated into (note that this used the
original LaTeX source).
Learning Go's source has been rewritten in mmark2 [6], but did not
see any other changes. This books translates cleanly into an RFC-
like document [7].
Copyright Notice
Copyright (c) 2018 IETF Trust and the persons identified as the
document authors. All rights reserved.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 6
1.1. How to Read this Book . . . . . . . . . . . . . . . . . . 7
1.2. Official Documentation . . . . . . . . . . . . . . . . . 8
2. Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.1. Hello World . . . . . . . . . . . . . . . . . . . . . . . 9
2.2. Compiling and Running Code . . . . . . . . . . . . . . . 10
2.3. Variables, Types and Keywords . . . . . . . . . . . . . . 10
2.3.1. Boolean Types . . . . . . . . . . . . . . . . . . . . 11
2.3.2. Numerical Types . . . . . . . . . . . . . . . . . . . 11
2.3.3. Constants . . . . . . . . . . . . . . . . . . . . . . 12
2.3.4. Strings . . . . . . . . . . . . . . . . . . . . . . . 12
2.3.5. Runes . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3.6. Complex Numbers . . . . . . . . . . . . . . . . . . . 13
2.3.7. Errors . . . . . . . . . . . . . . . . . . . . . . . 13
2.4. Operators and Built-in Functions . . . . . . . . . . . . 13
2.5. Go Keywords . . . . . . . . . . . . . . . . . . . . . . . 14
2.6. Control Structures . . . . . . . . . . . . . . . . . . . 15
2.6.1. If-Else . . . . . . . . . . . . . . . . . . . . . . . 15
2.6.2. Goto . . . . . . . . . . . . . . . . . . . . . . . . 16
2.6.3. For . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.6.4. Break and Continue . . . . . . . . . . . . . . . . . 16
2.6.5. Range . . . . . . . . . . . . . . . . . . . . . . . . 17
2.6.6. Switch . . . . . . . . . . . . . . . . . . . . . . . 18
2.7. Built-in Functions . . . . . . . . . . . . . . . . . . . 19
2.8. Arrays, Slices, and Maps . . . . . . . . . . . . . . . . 20
2.8.1. Arrays . . . . . . . . . . . . . . . . . . . . . . . 20
2.8.2. Slices . . . . . . . . . . . . . . . . . . . . . . . 21
2.8.3. Maps . . . . . . . . . . . . . . . . . . . . . . . . 23
2.9. Exercises . . . . . . . . . . . . . . . . . . . . . . . . 25
2.9.1. For-loop . . . . . . . . . . . . . . . . . . . . . . 25
2.9.2. Answer . . . . . . . . . . . . . . . . . . . . . . . 25
2.9.3. Average . . . . . . . . . . . . . . . . . . . . . . . 26
2.9.4. Answer . . . . . . . . . . . . . . . . . . . . . . . 26
2.9.5. FizzBuzz . . . . . . . . . . . . . . . . . . . . . . 27
2.9.6. Answer . . . . . . . . . . . . . . . . . . . . . . . 27
3. Functions . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.1. Scope . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.2. Functions as values . . . . . . . . . . . . . . . . . . . 31
3.3. Callbacks . . . . . . . . . . . . . . . . . . . . . . . . 31
3.4. Deferred Code . . . . . . . . . . . . . . . . . . . . . . 32
3.5. Variadic Parameter . . . . . . . . . . . . . . . . . . . 34
3.6. Panic and recovering . . . . . . . . . . . . . . . . . . 34
3.7. Exercises . . . . . . . . . . . . . . . . . . . . . . . . 36
3.7.1. Average . . . . . . . . . . . . . . . . . . . . . . . 36
3.7.2. Answer . . . . . . . . . . . . . . . . . . . . . . . 36
3.7.3. Bubble sort . . . . . . . . . . . . . . . . . . . . . 37
3.7.4. Answer . . . . . . . . . . . . . . . . . . . . . . . 37
3.7.5. For-loop II . . . . . . . . . . . . . . . . . . . . . 38
3.7.6. Answer . . . . . . . . . . . . . . . . . . . . . . . 38
3.7.7. Fibonacci . . . . . . . . . . . . . . . . . . . . . . 38
3.7.8. Answer . . . . . . . . . . . . . . . . . . . . . . . 38
3.7.9. Var args . . . . . . . . . . . . . . . . . . . . . . 39
3.7.10. Answer . . . . . . . . . . . . . . . . . . . . . . . 39
3.7.11. Functions that return functions . . . . . . . . . . . 39
3.7.12. Answer . . . . . . . . . . . . . . . . . . . . . . . 39
3.7.13. Maximum . . . . . . . . . . . . . . . . . . . . . . . 40
3.7.14. Answer . . . . . . . . . . . . . . . . . . . . . . . 40
3.7.15. Map function . . . . . . . . . . . . . . . . . . . . 40
3.7.16. Answer . . . . . . . . . . . . . . . . . . . . . . . 41
3.7.17. Stack . . . . . . . . . . . . . . . . . . . . . . . . 41
3.7.18. Answer . . . . . . . . . . . . . . . . . . . . . . . 41
4. Packages . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.1. Identifiers . . . . . . . . . . . . . . . . . . . . . . . 45
4.2. Documenting packages . . . . . . . . . . . . . . . . . . 46
4.3. Testing packages . . . . . . . . . . . . . . . . . . . . 47
4.4. Useful packages . . . . . . . . . . . . . . . . . . . . . 49
4.5. Exercises . . . . . . . . . . . . . . . . . . . . . . . . 51
4.5.1. Stack as package . . . . . . . . . . . . . . . . . . 51
4.5.2. Answer . . . . . . . . . . . . . . . . . . . . . . . 51
4.5.3. Calculator . . . . . . . . . . . . . . . . . . . . . 52
4.5.4. Answer . . . . . . . . . . . . . . . . . . . . . . . 52
5. Beyond the basics . . . . . . . . . . . . . . . . . . . . . . 54
5.1. Allocation . . . . . . . . . . . . . . . . . . . . . . . 55
5.1.1. Allocation with new . . . . . . . . . . . . . . . . . 55
5.1.2. Allocation with make . . . . . . . . . . . . . . . . 55
5.1.3. Constructors and composite literals . . . . . . . . . 56
5.2. Defining your own types . . . . . . . . . . . . . . . . . 57
5.2.1. More on structure fields . . . . . . . . . . . . . . 58
5.2.2. Methods . . . . . . . . . . . . . . . . . . . . . . . 59
5.3. Conversions . . . . . . . . . . . . . . . . . . . . . . . 60
5.3.1. User defined types and conversions . . . . . . . . . 61
5.4. Exercises . . . . . . . . . . . . . . . . . . . . . . . . 62
5.4.1. Map function with interfaces . . . . . . . . . . . . 62
5.4.2. Answer . . . . . . . . . . . . . . . . . . . . . . . 62
5.4.3. Pointers . . . . . . . . . . . . . . . . . . . . . . 63
5.4.4. Answer . . . . . . . . . . . . . . . . . . . . . . . 63
5.4.5. Linked List . . . . . . . . . . . . . . . . . . . . . 63
5.4.6. Answer . . . . . . . . . . . . . . . . . . . . . . . 64
5.4.7. Cat . . . . . . . . . . . . . . . . . . . . . . . . . 66
5.4.8. Answer . . . . . . . . . . . . . . . . . . . . . . . 66
5.4.9. Method calls . . . . . . . . . . . . . . . . . . . . 70
5.4.10. Answer . . . . . . . . . . . . . . . . . . . . . . . 70
6. Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.1. Which is what? . . . . . . . . . . . . . . . . . . . . . 72
1. Introduction
Concurrent
Go makes it easy to "fire off" functions to be run as _very_
lightweight threads. These threads are called goroutines in Go.
Channels
Communication with these goroutines is done, either via shared
state or via channels [csp].
Fast
Compilation is fast and execution is fast. The aim is to be as
fast as C. Compilation time is measured in seconds.
Safe
Explicit casting and strict rules when converting one type to
another. Go has garbage collection. No more "free()" in Go: the
language takes care of this.
Standard format
A Go program can be formatted in (almost) any way the programmers
want, but an official format exists. The rule is very simple: The
output of the filter "gofmt" _is the officially endorsed format_.
Postfix types
Types are given _after_ the variable name, thus "var a int",
instead of "int a".
UTF-8
UTF-8 is everywhere, in strings _and_ in the program code.
Finally you can use "\Phi = \Phi + 1" in your source code.
Open Source
The Go license is completely open source.
Fun
Programming with Go should be fun!
I've written this book for people who already know some programming
languages and how to program. In order to use this book, you (of
course) need Go installed on your system, but you can easily try
examples online in the Go playground. All exercises in this book
work with Go 1, the first stable release of Go -- if not, it's a bug.
basics
We'll look at the basic types, variables, and control structures
available in the language.
functions
Here we look at functions, the basic building blocks of Go
programs.
packages
We'll see that functions and data can be grouped together in
packages. We'll also see how to document and test our packages.
beyond-the-basics
We'll create our own types. We'll also look at memory allocations
in Go.
interfaces
We'll learn how to use interfaces. Interfaces are the central
concept in Go, as Go does not support object orientation in the
traditional sense.
concurrency
We'll learn the "go" keyword, which can be used to start function
in separate routines (called goroutines). Communication with
those goroutines is done via channels.
communication
Finally we'll see how to interface with the rest of the world from
within a Go program. We'll see how to create files and read and
write to and from them. We'll also briefly look into networking.
% godoc builtin
% godoc hash
PACKAGE DOCUMENTATION
package fnv
import "hash/fnv"
2. Basics
Lets look at the program line by line. This first line is just
required _1_. All Go files start with "package <something>", and
"package main" is required for a standalone executable.
Next we another comment, but this one is enclosed in "/*" "*/" _3_.
When your Go program is executed, the first function called will be
"main.main()", which mimics the behavior from C. Here we declare
that function _4_.
% go build helloworld.go
% ./helloworld
Hello, world.
You can combine the above and just call "go run helloworld.go".
In the next few sections we will look at the variables, basic types,
keywords, and control structures of our new language.
var a int a := 15
var b bool b := false
a = 15
b = false
On the left we use the "var" keyword to declare a variable and _then_
assign a value to it. The code on the right uses ":=" to do this in
one step (this form may only be used _inside_ functions). In that
case the variable type is _deduced_ from the value. A value of 15
indicates an "int". A value of "false" tells Go that the type should
be "bool". Multiple "var" declarations may also be grouped; "const"
(see Section 2.3.3) and "import" also allow this. Note the use of
parentheses instead of braces:
var (
x int
b bool
)
Go has most of the well-known types such as "int". The "int" type
has the appropriate length for your machine, meaning that on a 32-bit
machine it is 32 bits and on a 64-bit machine it is 64 bits. Note:
an "int" is either 32 or 64 bits, no other values are defined. Same
goes for "uint", the unsigned int.
If you want to be explicit about the length, you can have that too,
with "int32", or "uint32". The full list for (signed and unsigned)
integers is "int8", "int16", "int32", "int64" and "byte", "uint8",
"uint16", "uint32", "uint64", with "byte" being an alias for "uint8".
For floating point values there is "float32" and "float64" (there is
no "float" type). A 64 bit integer or floating point value is
_always_ 64 bit, also on 32 bit architectures.
Note that these types are all distinct and assigning variables which
mix these types is a compiler error, like in the following code:
package main
func main() {
var a int
var b int32
b = a + a
b = b + 5
}
2.3.3. Constants
const (
a = iota
b
)
2.3.4. Strings
s := "Hello World!"
s := "hello"
c := []rune(s) <1>
c[0] = 'c' <2>
s2 := string(c) <3>
fmt.Printf("%s\n", s2) <4>
2.3.5. Runes
Go has native support for complex numbers. To use them you need a
variable of type "complex128" (64 bit real and imaginary parts) or
"complex64" (32 bit real and imaginary parts). Complex numbers are
written as "re + im""i", where "re" is the real part, "im" is the
imaginary part and "i" is the literal '"i"' ("\sqrt{-1}").
2.3.7. Errors
Any non-trivial program will have the need for error reporting sooner
or later. Because of this Go has a builtin type specially for
errors, called "error". "var e error" creates a variable "e" of type
"error" with the value "nil". This error type is an interface --
we'll look more at interfaces in Section 6. For now you can just
assume that "error" is a type just like all other types.
+------------+--------------------------+
| Precedence | Operator(s) |
+------------+--------------------------+
| Highest | "* / % << >> & &^" |
| | `+ - |
| | "== != < <= > >=" |
| | "<-" |
| | "&&" |
| Lowest | || |
+------------+--------------------------+
"+ - * /" and "%" all do what you would expect, "& | ^" and "&^" are
bit operators for bitwise _and_ bitwise _or_ bitwise _xor_ and bit
clear respectively. The "&&" and "||" operators are logical _and_
and logical _or_ Not listed in the table is the logical not "!"
2.5. Go Keywords
+-------------+----------------+-----------+-------------+----------+
| "break" | "default" | "func" | "interface" | "select" |
| "case" | "defer" | "go" | "map" | "struct" |
| "chan" | "else" | "goto" | "package" | "switch" |
| "const" | "fallthrough" | "if" | "range" | "type" |
| "continue" | "for" | "import" | "return" | "var" |
+-------------+----------------+-----------+-------------+----------+
We've seen some of these already. We used "var" and "const" in the
Section 2.3 section, and we briefly looked at "package" and "import"
in our "Hello World" program at the start of the chapter. Others
need more attention and have their own chapter or section:
There are only a few control structures in Go. To write loops we use
the "for" keyword, and there is a "switch" and of course an "if".
When working with channels "select" will be used (see Section 7.3.1).
Parentheses are not required around the condition, and the body must
_always_ be brace-delimited.
2.6.1. If-Else
if x > 0 {
return y
} else {
return x
}
The opening brace on the first line must be positioned on the same
line as the "if" statement. There is no arguing about this, because
this is what "gofmt" outputs.
2.6.2. Goto
func myfunc() {
i := 0
Here:
fmt.Println(i)
i++
goto Here
}
The string "Here:" indicates a label. A label does not need to start
with a capital letter and is case sensitive.
2.6.3. For
The Go "for" loop has three forms, only one of which has semicolons:
o "for init; condition; post { }" - a loop using the syntax borrowed
from C;
sum := 0
for i := 0; i < 10; i++ {
sum = sum + i
}
Note that the variable "i" ceases to exist after the loop.
With "break" you can quit loops early. By itself, "break" breaks the
current loop.
Here we "break" the current loop _1_, and don't continue with the
"fmt.Println(i)" statement _2_. So we only print 0 to 5. With loops
within loop you can specify a label after "break" to identify _which_
loop to stop:
Here we define a label "J" _1_, preceding the "for"-loop there. When
we use "break J" _2_, we don't break the inner loop but the "J" loop.
With "continue" you begin the next iteration of the loop, skipping
any remaining code. In the same way as "break", "continue" also
accepts a label.
2.6.5. Range
The keyword "range" can be used for loops. It can loop over slices,
arrays, strings, maps and channels (see Section 7.3.1). "range" is an
iterator that, when called, returns the next key-value pair from the
"thing" it loops over. Depending on what that is, "range" returns
different things.
When looping over a slice or array, "range" returns the index in the
slice as the key and value belonging to that index. Consider this
code:
and the key as a "string". It will start with 0 and "a", so "k" will
be 0 through 5, and v will be "a" through "f".
You can also use "range" on strings directly. Then it will break out
the individual Unicode characters ^[In the UTF-8 world characters are
sometimes called _runes_ Mostly, when people talk about characters,
they mean 8 bit characters. As UTF-8 characters may be up to 32 bits
the word rune is used. In this case the type of "char" is "rune".
and their start position, by parsing the UTF-8. The loop:
prints
2.6.6. Switch
Go's "switch" is very flexible; you can match on much more than just
integers. The cases are evaluated top to bottom until a match is
found, and if the "switch" has no expression it switches on "true".
It's therefore possible -- and idiomatic -- to write an "if-else-if-
else" chain as a "switch".
switch i {
case 0: fallthrough
case 1: <1>
f()
default:
g() <2>
"f()" can be called when "i == 0" _1_. With "default" you can specify
an action when none of the other cases match. Here "g()" is called
when "i" is not 0 or 1 _2_. We could rewrite the above example as:
switch i {
case 0, 1: <1>
f()
default:
g()
+----------+----------+-----------+-----------+
| "close" | "new" | "panic" | "complex" |
| "delete" | "make" | "recover" | "real" |
| "len" | "append" | "print" | "imag" |
| "cap" | "copy" | "println" | |
+----------+----------+-----------+-----------+
close
is used in channel communication. It closes a channel. We'll
learn more about this in Section 7.3.1.
delete
is used for deleting entries in maps.
new
is used for allocating memory for user defined data types. See
Section 5.1.1.
make
is used for allocating memory for built-in types (maps, slices,
and channels). See Section 5.1.2.
copy, append
"copy" is for copying slices. And "append" is for concatenating
slices. See Section 2.8.2 in this chapter.
panic, recover
are used for an _exception_ mechanism. See Section 3.6 for more.
print, println
are low level printing functions that can be used without
reverting to the "fmt" package. These are mainly used for
debugging. built-in,println)
To store multiple values in a list, you can use arrays, or their more
flexible cousin: slices. A dictionary or hash type is also
available. It is called a "map" in Go.
2.8.1. Arrays
Array types like "var arr [10]int" have a fixed size. The size is
_part_ of the type. They can't grow, because then they would have a
2.8.2. Slices
A slice is similar to an array, but it can grow when new elements are
added. A slice always refers to an underlying array. What makes
slices different from arrays is that a slice is a pointer _to_ an
array; slices are reference types.
That means that if you assign one slice to another, both refer to the
_same_ underlying array. For instance, if a function takes a slice
argument, changes it makes to the elements of the slice will be
visible to the caller, analogous to passing a pointer to the
underlying array. With: "slice := make([]int, 10)", you create a
slice which can hold ten elements. Note that the underlying array
isn't specified. A slice is always coupled to an array that has a
fixed size. For slices we define a capacity and a length The image
below shows the creation of an array, then the creation of a slice.
First we create an array of "m" elements of the type "int": "var
array[m]int" .
o "len(slice) == n"
o "cap(slice) == m"
a := [...]int{1, 2, 3, 4, 5} <1>
s1 := a[2:4] <2>
s2 := a[1:5] <3>
s3 := a[:] <4>
s4 := a[:4] <5>
s5 := s2[:] <6>
s6 := a[2:4:5] <7>
When working with slices you can overrun the bounds, consider this
code.
package main
func main() {
var array [100]int <1>
slice := array[0:99] <2>
slice[98] = 1 <3>
slice[99] = 2 <4>
}
the 99th element _3_ of the slice. This works as expected. But at
_4_ we dare to do the impossible, and and try to allocate something
beyond the length of the slice and we are greeted with a _runtime_
error: "Error: "throw: index out of range"."
s0 := []int{0, 0}
s1 := append(s0, 2) <1>
s2 := append(s1, 3, 5, 7) <2>
s3 := append(s2, s0...) <3>
var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
n1 := copy(s, a[0:]) <1>
n2 := copy(s, s[2:]) <2>
2.8.3. Maps
monthdays := map[string]int{
"Jan": 31, "Feb": 28, "Mar": 31,
"Apr": 30, "May": 31, "Jun": 30,
"Jul": 31, "Aug": 31, "Sep": 30,
"Oct": 31, "Nov": 30, "Dec": 31, <1>
}
year := 0
for _, days := range monthdays <1>
year += days
}
fmt.Printf("Numbers of days in a year: %d\n", year)
To add elements to the map, you would add new month with:
"monthdays["Undecim"] = 30". If you use a key that already exists,
the value will be silently overwritten: "monthdays["Feb"] = 29". To
test for existence you would use the following: "value, present :=
monthdays["Jan"]". If the key "Jan" exists, "present" will be true.
It's more Go like to name "present" "ok", and use: "v, ok :=
monthdays["Jan"]". In Go we call this the "comma ok" form.
2.9. Exercises
2.9.1. For-loop
2. Rewrite the loop from 1 to use "goto". The keyword "for" may not
be used.
3. Rewrite the loop again so that it fills an array and then prints
that array to the screen.
2.9.2. Answer
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
fmt.Println("%d", i)
}
}
% go build for.go
% ./for
0
1
.
.
.
9
func main() {
i := 0 <1>
Loop: <2>
if i < 10 {
fmt.Printf("%d\n", i)
i++
goto Loop <3>
}
}
At _1_ we define our loop variable. And at _2_ we define a label
and at _3_ we jump to this label.
package main
import "fmt"
func main() {
var arr [10]int <1>
for i := 0; i < 10; i++ {
arr[i] = i <2>
}
fmt.Printf("%v", arr) <3>
}
Here _1_ we create an array with 10 elements. Which we then fill
_2_ one by one. And finally we print it _3_ with "%v" which lets
Go to print the value for us. You could even do this in one fell
swoop by using a composite literal:
fmt.Printf("%v\n", [...]int{0,1,2,3,4,5,6,7,8,9})
2.9.3. Average
2.9.4. Answer
sum := 0.0
switch len(xs) {
case 0: <1>
avg = 0
default: <2>
for _, v := range xs {
sum += v
}
avg = sum / float64(len(xs)) <3>
}
2.9.5. FizzBuzz
Write a program that prints the numbers from 1 to 100. But for
multiples of three print, "Fizz" instead of the number, and for
multiples of five, print "Buzz". For numbers which are multiples of
both three and five, print "FizzBuzz".
2.9.6. Answer
package main
import "fmt"
func main() {
const (
FIZZ = 3 <1>
BUZZ = 5
)
var p bool <2>
for i := 1; i < 100; i++ { <3>
p = false
if i%FIZZ == 0 { <4>
fmt.Printf("Fizz")
p = true
}
if i%BUZZ == 0 { <5>
fmt.Printf("Buzz")
p = true
}
if !p { <6>
fmt.Printf("%v", i)
}
fmt.Println()
}
}
Here _1_ we define two constants to make our code more readable, see
Section 2.3.3. At _2_ we define a boolean that keeps track if we
already printed something. At _3_ we start our for-loop, see
Section 2.6.3. If the value is divisible by FIZZ - that is, 3 - , we
print "Fizz" _4_. And at _5_ we check if the value is divisble by
BUZZ -- that is, 5 -- if so print "Buzz". Note that we have also
taken care of the FizzBuzz case. At _6_, if printed neither Fizz nor
Buzz printed, we print the value.
3. Functions
Richard P. Gabriel
To declare a function, you use the "func" keyword _1_. You can
optionally bind _2_ to a specific type called receiver (a function
with a receiver is usually called a method). This will be explored
in Section 6. Next _3_ you write the name of your function. Here
_4_ we define that the variable "q" of type "int" is the input
parameter. Parameters are passed _pass-by-value_. The variables "r"
and "s" _5_ are the _named return parameters_ (((functions, named
return parameters))) for this function. Functions in Go can have
multiple return values. This is very useful to return a value _and_
error. This removes the need for in-band error returns (such as -1
for "EOF") and modifying an argument. If you want the return
parameters not to be named you only give the types: "(int, int)". If
you have only one value to return you may omit the parentheses. If
your function is a subroutine and does not have anything to return
you may omit this entirely. Finally, we have the body _6_ of the
function. Note that "return" is a statement so the braces around the
parameter(s) are optional.
The names are not mandatory but they can make code shorter and
clearer: _they are documentation_. However don't overuse this
feature, especially in longer functions where it might not be
immediately apparent what is returned.
Functions can be declared in any order you wish. The compiler scans
the entire file before execution, so function prototyping is a thing
of the past in Go. Go does not allow nested functions, but you can
work around this with anonymous functions. See the
Section Section 3.2 in this chapter. Recursive functions work just
as in other languages:
Here _2_ we call the same function again, "rec" returns when "i" has
the value 10, this is checked on the second line _1_. This function
prints: "9 8 7 6 5 4 3 2 1 0", when called as "rec(0)".
3.1. Scope
package main
func main() {
a = 5
print(a)
f()
}
func f() {
a := 6 <2>
print(a)
g()
}
func g() {
print(a)
}
the output will be: "565". A _local_ variable is _only_ valid when
we are executing the function in which it is defined. Note that the
":=" used in line 12 is sometimes hard to spot so it is generally
advised _not_ to use the same name for global and local variables.
import "fmt"
func main() {
a := func() { <1>
fmt.Println("Hello")
} <2>
a() <3>
}
3.3. Callbacks
Because functions are values they are easy to pass to functions, from
where they can be used as callbacks. First define a function that
does "something" with an integer value:
This function does not return a value and just prints its argument.
The _signature_ of this function is: "func printit(int)", or without
the function name: "func(int)". To create a new function that uses
this one as a callback we need to use this signature:
Here we create a new function that takes two parameters: "y int",
i.e. just an "int" and "f func(int)", i.e. a function that takes an
int and returns nothing. The parameter "f" is the variable holding
that function. It can be used as any other function, and we execute
the function on line 2 with the parameter "y": "f(y)"
Suppose you have a function in which you open a file and perform
various writes and reads on it. In such a function there are often
spots where you want to return early. If you do that, you will need
to close the file descriptor you are working on. This often leads to
the following code:
if failureY {
file.Close() <1>
return false
}
file.Close() <1>
return true <2>
}
Note that we repeat a lot of code here; you can see the that
"file.Close()" is called at _1_. To overcome this, Go has the "defer"
keyword. After "defer" you specify a function which is called just
_before_ _2_ the current function exits.
With "defer" we can rewrite the above code as follows. It makes the
function more readable and it puts the "Close" _right next_ to the
"Open".
You can put multiple functions on the "defer list", like this example
from
With "defer" you can even change return values, provided that you are
using named result parameters and a function literal , i.e:
Here we use a function without a name and specify the body of the
function inline, basically we're creating a nameless function on the
spot. The final braces are needed because "defer" needs a function
call, not a function value. If our anonymous function would take an
parameter it would be easier to see why we need the braces:
In this (unnamed) function you can access any named return parameter:
Here _1_ we specify our function, the named return value "ret" is
initialized with zero. The nameless function in the defer increments
the value of "ret" with 1. The "return 0" on line 5 _will not be the
returned value_, because of "defer". The function "f" will return 1!
We range over the arguments on the first line. We are not interested
in the index as returned by "range", hence the use of the underscore
there. In the body of the "range" we just print the parameters we
were given.
powerful tool: use it wisely. So, how do you use it? In the words
of the Go Authors [go_blog_panic]:
Panic
is a built-in function that stops the ordinary flow of control and
begins panicking. When the function "F" calls "panic", execution
of "F" stops, any deferred functions in "F" are executed normally,
and then "F" returns to its caller. To the caller, "F" then
behaves like a call to "panic". The process continues up the
stack until all functions in the current goroutine have returned,
at which point the program crashes. Panics can be initiated by
invoking "panic" directly. They can also be caused by _runtime
errors_, such as out-of-bounds array accesses.
Recover
is a built-in function that regains control of a panicking
goroutine. Recover is _only_ useful inside _deferred_ functions.
During normal execution, a call to "recover" will return "nil" and
have no other effect. If the current goroutine is panicking, a
call to "recover" will capture the value given to "panic" and
resume normal execution.
The following code fragment, shows how we can use this function:
func panicy() {
var a []int
a[3] = 5
}
func main() {
fmt.Println(Panic(panicy))
}
3.7. Exercises
3.7.1. Average
3.7.2. Answer
package main
3.7.4. Answer
func main() {
n := []int{5, -1, 0, 12, 3, 5}
fmt.Printf("unsorted %v\n", n)
bubblesort(n)
fmt.Printf("sorted %v\n", n)
}
3.7.5. For-loop II
1. Take what you did in exercise to write the for loop and extend it
a bit. Put the body of the for loop - the "fmt.Printf" - in a
separate function.
3.7.6. Answer
1. <{{src/for-func.go}}
3.7.7. Fibonacci
3.7.8. Answer
package main
import "fmt"
func main() {
for _, term := range fibonacci(10) { <5>
fmt.Printf("%v ", term)
}
}
3.7.10. Answer
package main
import "fmt"
func main() {
printthem(1, 4, 5, 7, 4)
printthem(1, 2, 4)
}
p := plusTwo()
fmt.Printf("%v\n", p(2))
Which should print 4. See Section 3.3.
3.7.12. Answer
func main() {
p2 := plusTwo()
fmt.Printf("%v\n",p2(2))
}
3.7.13. Maximum
3.7.14. Answer
3.7.16. Answer
1. A possible answer:
func main() {
m := []int{1, 3, 4}
f := func(i int) int {
return i * i
}
fmt.Printf("%v", (Map(f, m)))
}
3.7.17. Stack
fig/stack.png A stack
A stack.
3.7.18. Answer
Next we need the "push" and "pop" functions to actually use the
thing. First we show the _wrong_ solution!
The function works on the "s" which is of the type "stack". To use
this we just call "s.push(50)", to push the integer 50 on the stack.
But the push function gets a copy of "s", so it is _not_ working on
the _real_ thing. Nothing gets pushed to our stack. For example the
following code:
var s stack
s.push(25)
fmt.Printf("stack %v\n", s);
s.push(14)
fmt.Printf("stack %v\n", s);
prints:
stack [0:0]
stack [0:0]
to
func main() {
var s stack
s.push(25)
s.push(14)
fmt.Printf("stack %v\n", s)
}
4. Packages
"^(")
------------------------------------------------------------
You declare a package with the "package" keyword. The filename does
not have to match the package name. The convention for package names
is to use lowercase characters. Go packages may consist of multiple
files, but they share the "package <name>" line. Let's define a
package "even" in the file "even.go".
Here _1_ we start a new namespace: "even". The function "Even" _2_
starts with a capital letter. This means the function is _exported_,
and may be used outside our package (more on that later). The
function "odd" _3_ does not start with a capital letter, so it is a
_private_ function.
% mkdir $GOPATH/src/even
% cp even.go $GOPATH/src/even
% go build
% go install
package main
import ( <1>
"even" <2>
"fmt" <3>
)
func main() {
i := 5
fmt.Printf("Is %d even? %v\n", i, even.Even(i)) <4>
}
now we use _4_ the function from the "even" package. The syntax for
accessing a function from a package is "<package>.FunctionName()".
And finally we can build our program.
% go build myeven.go
% ./myeven
Is 5 even? false
4.1. Identifiers
The Go standard library names some function with the old (Unix) names
while others are in CamelCase. The convention is to leave well-known
legacy not-quite-words alone rather than try to figure out where the
capital letters go: "Atoi", "Getwd", "Chmod". CamelCasing works best
when you have whole words to work with: "ReadFile", "NewWriter",
"MakeSlice". The convention in Go is to use CamelCase rather than
underscores to write multi-word names.
The package name used in the "import" statement is the default name
used. But if the need arises (two different packages with the same
name for instance), you can override this default: "import bar
"bytes"" The function "Buffer" is now accessed as "bar.Buffer".
Another convention is that the package name is the base name of its
source directory; the package in "src/compress/gzip" is imported as
"compress/gzip" but has name "gzip", not "compress/gzip".
When running "go doc" this will show up at the top of the page. When
a package consists of multiple files the package comment should only
appear in one file. A common convention (in really big packages) is
to have a separate "doc.go" that only holds the package comment.
Here is a snippet from the official "regexp" package:
/*
The regexp package implements a simple library for
regular expressions.
regexp:
concatenation { '|' concatenation }
*/
package regexp
Each defined (and exported) function should have a small line of text
documenting the behavior of the function. Again to extend our "even"
package:
And even though "odd" is not exported, it's good form to document it
as well.
The "go test" program runs all the test functions. Without any
defined tests for our "even" package, "go test" yields:
% go test
? even [no test files]
Let us fix this by defining a test in a test file. Test files reside
in the package directory and are named "*_test.go". Those test files
are just like other Go programs, but "go test" will only execute the
test functions. Each test function has the same signature and its
name should start with "Test": "func TestXxx(t *testing.T)" .
When writing test you will need to tell "go test" whether a test was
successful or not. A successful test function just returns. When
the test fails you can signal this with the following functions.
These are the most important ones (see "go doc testing" or "go help
testfunc" for more):
Putting all this together we can write our test. First we pick a
name: "even_test.go". Then we add the following contents:
A test file belongs to the current _1_ package. This is not only
convenient, but also allows tests of unexported functions and
structures. We then _2_ import the "testing" package. And finally
the test we want to execute. The code here _3_ should hold no
surprises: we check if the "Even" function works OK. And now, the
moment we have been waiting for executing the test.
% go test
ok even 0.001s
Our test ran and reported "ok". Success! If we redefine our test
function, we can see the result of a failed test:
We now get:
And you can act accordingly (by fixing the test for instance).
func ExampleEven() {
if Even(2) {
fmt.Printf("Is even\n")
}
// Output: <1>
// Is even
}
Those last two comments lines _1_ are part of the example, "go test"
uses those to check the _generated_ output with the text in the
comments. If there is a mismatch the test fails.
fmt
Package "fmt" implements formatted I/O with functions analogous to
C's "printf" and "scanf". The format verbs are derived from C's
but are simpler. Some verbs (%-sequences) that can be used:
io
This package provides basic interfaces to I/O primitives. Its
primary job is to wrap existing implementations of such
primitives, such as those in package os, into shared public
interfaces that abstract the functionality, plus some other
related primitives.
bufio
This package implements buffered I/O. It wraps an "io.Reader" or
"io.Writer" object, creating another object (Reader or Writer)
that also implements the interface but provides buffering and some
help for textual I/O.
sort
The "sort" package provides primitives for sorting arrays and
user-defined collections.
strconv
The "strconv" package implements conversions to and from string
representations of basic data types.
os
The "os" package provides a platform-independent interface to
operating system functionality. The design is Unix-like.
sync
The package "sync" provides basic synchronization primitives such
as mutual exclusion locks.
flag
The "flag" package implements command-line flag parsing.
encoding/json
The "encoding/json" package implements encoding and decoding of
JSON objects as defined in RFC 4627 [RFC4627].
html/template
Data-driven templates for generating textual output such as HTML.
Templates are executed by applying them to a data structure.
Annotations in the template refer to elements of the data
structure (typically a field of a struct or a key in a map) to
control execution and derive values to be displayed. The template
walks the structure as it executes and the "cursor" @ represents
the value at the current location in the structure.
net/http
The "net/http" package implements parsing of HTTP requests,
replies, and URLs and provides an extensible HTTP server and a
basic HTTP client.
unsafe
The "unsafe" package contains operations that step around the type
safety of Go programs. Normally you don't need this package, but
it is worth mentioning that _unsafe_ Go programs are possible.
reflect
The "reflect" package implements run-time reflection, allowing a
program to manipulate objects with arbitrary types. The typical
use is to take a value with static type "interface{}" and extract
its dynamic type information by calling "TypeOf", which returns an
os/exec
The "os/exec" package runs external commands.
4.5. Exercises
2. Write a simple unit test for this package. You should at least
test that a "Pop" works after a "Push".
4.5.2. Answer
package stack
package stack
import "testing"
For "go test" to work we need to put our package files in a directory
under "$GOPATH/src":
% mkdir $GOPATH/src/stack
% cp pushpop_test.go $GOPATH/src/stack
% cp stack-as-package.go $GOPATH/src/stack
Yields:
% go test stack
ok stack 0.001s
4.5.3. Calculator
4.5.4. Answer
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
for {
s, err := reader.ReadString('\n')
var token string
if err != nil {
return
}
for _, c := range s {
switch {
case c >= '0' && c <= '9':
token = token + string(c)
case c == ' ':
r, _ := strconv.Atoi(token)
st.push(r)
token = ""
case c == '+':
fmt.Printf("%d\n", st.pop()+st.pop())
case c == '*':
fmt.Printf("%d\n", st.pop()*st.pop())
case c == '-':
p := st.pop()
q := st.pop()
fmt.Printf("%d\n", q-p)
case c == 'q':
return
default:
//error
}
}
}
}
var p *int
fmt.Printf("%v", p) <1>
fmt.Printf("%v", p) <4>
This _1_ Prints "nil". Declare _2_ an integer variable "i". Make
"p" point _3_ to "i", i.e. take the address of "i". And this _4_
will print something like "0x7ff96b81c000a". De-referencing a
pointer is done by prefixing the pointer variable with "*".
5.1. Allocation
Go also has garbage collection, meaning that you don't have to worry
about memory deallocation.
The documentation for "bytes.Buffer" states that "the zero value for
Buffer is an empty buffer ready to use.". Similarly, "sync.Mutex"
does not have an explicit constructor or Init method. Instead, the
zero value for a "sync.Mutex" is defined to be an unlocked mutex.
Allocates _1_ slice structure; rarely useful. "v" _2_ refers to a new
array of 100 ints. At _3_ we make it unnecessarily complex, _4_ is
more idiomatic.
Remember that "make" applies only to maps, slices and channels and
does not return a pointer. To obtain an explicit pointer allocate
with "new".
And of course "make" is only used for slices, maps and channels.
Composite literals can also be created for arrays, slices, and maps,
with the field labels being indices or map keys as appropriate. In
these examples, the initializations work regardless of the values of
"Enone", and "Einval", as long as they are distinct:
Of course Go allows you to define new types, it does this with the
"type" keyword: "type foo int"
This creates a new type "foo" which acts like an "int". Creating
more sophisticated types is done with the "struct" keyword. An
example would be when we want record somebody's name ("string") and
age ("int") in a single structure and make it a new type:
package main
import "fmt"
func main() {
a := new(NameAge)
a.name = "Pete"
a.age = 42
fmt.Printf("%v\n", a)
}
fmt.Printf("%s", a.name)
struct {
x, y int
A *[]int
F func()
}
If you omit the name for a field, you create an anonymous field
(((field, anonymous))), for instance:
struct {
T1 // Field name is T1.
*T2 // Field name is T2.
P.T3 // Field name is T3.
x, y int // Field names are x and y.
}
Note that field names that start with a capital letter are exported,
i.e. can be set or read from other packages. Field names that start
with a lowercase are private to the current package. The same goes
for functions defined in packages, see Section 4 for the details.
5.2.2. Methods
If you create functions that work on your newly defined type, you can
take two routes:
var n *NameAge
n.doSomething(2)
In the above case this means that the following is _not_ an error:
Here Go will search the method list for "n" of type "NameAge", come
up empty and will then _also_ search the method list for the type
"*NameAge" and will translate this call to "(&n).doSomething(2)".
5.3. Conversions
+-------+---------+---------+---------+---------+--------+----------+
| From | "b | "i | "r | "s | "f flo | "i int" |
| | []byte" | []int" | []rune" | string" | at32" | |
+-------+---------+---------+---------+---------+--------+----------+
| *To* | | | | | | |
| "[]by | . | | | "[]byte | | |
| te" | | | | (s)" | | |
| "[]in | | . | | "[]int( | | |
| t" | | | | s)" | | |
| "[]ru | | | | "[]rune | | |
| ne" | | | | (s)" | | |
| "stri | "string | "string | "string | . | | |
| ng" | (b)" | (i)" | (r)" | | | |
| "floa | | | | | . | "float32 |
| t32" | | | | | | (i)" |
| "int" | | | | | "int(f | . |
| | | | | | )" | |
+-------+---------+---------+---------+---------+--------+----------+
runeslice := []rune(mystring)
Converts to an "rune" slice, each "rune" contains a Unicode code
point. Every character from the string corresponds to one rune.
How can you convert between the types you have defined yourself? We
create two types here "Foo" and "Bar", where "Bar" is an alias for
"Foo":
Then we:
Which fails on the last line with: "cannot use b (type bar) as type
foo in assignment"
5.4. Exercises
1. Use the answer from the earlier map exercise but now make it
generic using interfaces. Make it at least work for ints and
strings.
5.4.2. Answer
1.
package main
import "fmt"
func mult2(f e) e {
switch f.(type) {
case int:
return f.(int) * 2
case string:
return f.(string) + f.(string) + f.(string) + f.(string)
}
return f
}
func main() {
m := []e{1, 2, 3, 4}
s := []e{"a", "b", "c", "d"}
mf := Map(m, mult2)
sf := Map(s, mult2)
fmt.Printf("%v\n", mf)
fmt.Printf("%v\n", sf)
}
5.4.3. Pointers
var p1 Person
p2 := new(Person)
func Set(t T) {
x= &t
}
5.4.4. Answer
2. In the first function, "x" points to the same thing that "t"
does, which is the same thing that the actual argument points to.
So in the second function, we have an "extra" variable containing
a copy of the interesting value. In the second function, "x"
points to a new (heap-allocated) variable "t" which contains a
copy of whatever the actual argument value is.
2. Create your own linked list implementation. And perform the same
actions as above.
5.4.6. Answer
package main
import (
"container/list"
"fmt"
)
func main() {
l := list.New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(4)
package main
import (
"errors" <1>
"fmt"
)
return l
}
return v, err
}
func main() {
l := new(List)
l.Push(1)
l.Push(2)
l.Push(4)
fmt.Println()
fmt.Printf("%v\n", v)
}
}
5.4.7. Cat
5.4.8. Answer
package main
import (
"bufio"
"flag"
"fmt"
"io" <1>
"os"
)
func main() {
flag.Parse()
if flag.NArg() == 0 {
cat(bufio.NewReader(os.Stdin))
}
for i := 0; i < flag.NArg(); i++ {
f, e := os.Open(flag.Arg(i))
if e != nil {
fmt.Fprintf(os.Stderr, "%s: error reading from %s: %s\n",
os.Args[0], flag.Arg(i), e.Error())
continue
}
cat(bufio.NewReader(f))
}
}
At _1_ we include all the packages we need. Here _2_ we define a new
flag "n", which defaults to off. Note that we get the help (-h) for
free. Start the function _3_ that actually reads the file's contents
and displays it; Read one line at the time at _4_. And stop _5_ if we
hit the end. If we should number each line, print the line number
and then the line itself _6_. Otherwise _7_ we could just print the
line.
1. The bug show itself when the last line of the input does not
contain a newline. Or worse, when the input contains one line
without a closing newline nothing is shown at all. A better
solution is the following program.
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
)
func main() {
flag.Parse()
if flag.NArg() == 0 {
cat(bufio.NewReader(os.Stdin))
}
for i := 0; i < flag.NArg(); i++ {
f, e := os.Open(flag.Arg(i))
if e != nil {
fmt.Fprintf(os.Stderr, "%s: error reading from %s: %s\n",
os.Args[0], flag.Arg(i), e.Error())
continue
}
cat(bufio.NewReader(f))
}
}
package main
import "container/vector"
func main() {
k1 := vector.IntVector{}
k2 := &vector.IntVector{}
k3 := new(vector.IntVector)
k1.Push(2)
k2.Push(3)
k3.Push(4)
}
What are the types of "k1", "k2" and "k3"?
2. Now, this program compiles and runs OK. All the "Push"
operations work even though the variables are of a different
type. The documentation for "Push" says:
2.
So the receiver has to be of type "*IntVector", why does the code
above (the Push statements) work correctly then?
5.4.10. Answer
A method call "x.m()" is valid if the method set of (the type of)
"x" contains "m" and the argument list can be assigned to the
parameter list of "m". If "x" is addressable and "&x"'s method
set contains "m", "x.m()" is shorthand for "(&x).m()".
6. Interfaces
type I interface {
Get() int
Put(int)
}
A Go program can use this fact via yet another meaning of interface,
which is an interface value:
method at _3_. Because "S" implements "I", we can call the function
"f" passing in a pointer to a value of type "S": "var s S; f(&s)"
The reason we need to take the address of "s", rather than a value of
type "S", is because we defined the methods on "s" to operate on
pointers, see the definition in the code above. This is not a
requirement -- we could have defined the methods to take values --
but then the "Put" method would not work as expected.
The fact that you do not need to declare whether or not a type
implements an interface means that Go implements a form of duck
typing [duck_typing]. This is not pure duck typing, because when
possible the Go compiler will statically check whether the type
implements the interface. However, Go does have a purely dynamic
aspect, in that you can convert from one interface type to another.
In the general case, that conversion is checked at run time. If the
conversion is invalid -- if the type of the value stored in the
existing interface value does not satisfy the interface to which it
is being converted -- the program will fail with a run time error.
Let's define another type "R" that also implements the interface "I":
The function "f" can now accept variables of type "R" and "S".
Suppose you need to know the actual type in the function "f". In Go
you can figure that out by using a type switch.
func f(p I) {
switch t := p.(type) { <1>
case *S: <2>
case *R: <2>
default: <3>
}
}
At _1_ we use the type switch, note that the ".(type)" syntax is
_only_ valid within a "switch" statement. We store the value in the
variable "t". The subsequent cases _2_ each check for a different
_actual_ type. And we can even have a "default" _3_ clause. It is
worth pointing out that both "case R" and "case s" aren't possible,
because "p" needs to be a pointer in order to satisfy "i".
A type switch isn't the only way to discover the type at _run-time_.
if t, ok := something.(I); ok { <1>
// ...
}
You can also use a "comma, ok" form _1_ to see if an interface type
implements a specific interface. If "ok" is true, "t" will hold the
type of "something". When you are sure a variable implements an
interface you can use: "t := something.(I)" .
s = new(S)
fmt.Println(g(s));
The call to "g" will work fine and will print 0. If we however
invoke "g()" with a value that does not implement "I" we have a
problem:
var i int
fmt.Println(g(i))
This compiles, but when we run this we get slammed with: "panic:
interface conversion: int is not main.I: missing method Get".
Which is completely true, the built-in type "int" does not have a
"Get()" method.
6.3. Methods
Methods are functions that have a receiver (see Section 3). You can
define methods on any type (except on non-local types, this includes
built-in types: the type "int" can not have methods). You can
however make a new integer type with its own methods. For example:
The receiver type must be of the form "T" or "*T" where "T" is a
type name. "T" is called the receiver base type or just base
type. The base type must not be a pointer or interface type and
must be declared in the same package as the method.
There are a number of such names and it's productive to honor them
and the function names they capture. "Read", "Write", "Close",
"Flush", "String" and so on have canonical signatures and meanings.
To avoid confusion, don't give your method one of those names unless
it has the same signature and meaning. Conversely, if your type
implements a method with the same meaning as a method on a well-known
type, give it the same name and signature; call your string-converter
method "String" not "ToString".
is. And then _3_ then sort accordingly. And, when done, return _4_
the sorted slice.
o Define new types for the slices we want to sort. Note that we
declare slice types:
type Xi []int
type Xs []string
Sort(ints)
fmt.Printf("%v\n", ints)
Sort(strings)
fmt.Printf("%v\n", strings)
So on "t" we use "Elem()" to get the value the pointer points to. We
have now dereferenced the pointer and are "inside" our structure. We
then _4_ use "Field(0)" to access the zeroth field.
The struct "StructField" has a "Tag" member which returns the tag-
name as a string. So on the "0^{th}" field we can unleash ".Tag" _5_
to access this name: "Field(0).Tag". This gives us "namestr".
To make the difference between types and values more clear, take a
look at the following code:
At _1_ we create "t" the type data of "i", and "v" gets the actual
values at _2_. Here at _3_ we want to get to the "tag". So we need
"Elem()" to redirect the pointer, access the first field and get the
tag. Note that we operate on "t" a "reflect.Type". Now _4_ we want
to get access to the _value_ of one of the members and we employ
Peeling away the layers using reflection. Going from a *Person via
Elem using the methods described in go doc reflect to get the actual
string contained within.")
The first program compiles and runs, but when you run it, you are
greeted with a stack trace and a _run time_ error: "panic:
reflect.Value.SetString using value obtained using unexported field".
The second program works OK and sets the member "Name" to "Albert
Einstein". Of course this only works when you call "Set()" with a
pointer argument.
6.9. Exercises
6.9.1. Answer
package main
import "fmt"
func main() {
var a, b, c int = 5, 15, 0
var x, y, z float32 = 5.4, 29.3, 0.0
if c = a; Less(a, b) { <4>
c = b
}
if z = x; Less(x, y) { <4>
z = y
}
fmt.Println(c, z)
}
We could have chosen to make the return type of this _1_ function an
"interface{}", but that would mean that a caller would always have to
do a type assertion to extract the actual type from the interface.
At _2_ we compare the parameters. All parameters are confirmed to be
integers, so this is legit. And at _3_ we do the some for floats.
At _4_ we get the maximum value for "a", "b" and "x" and "y".
One of the last paragraphs in section Section 6.8 has the following
words:
The code on the right works OK and sets the member "Name" to
"Albert Einstein". Of course this only works when you call
"Set()" with a pointer argument.
6.9.3. Answer
7. Concurrency
func main() {
go ready("Tea", 2) //<1>
go ready("Coffee", 1) //<1>
fmt.Println("I'm waiting")
time.Sleep(5 * time.Second) //<2>
If we did not wait for the goroutines (i.e. remove the last line at
_2_) the program would be terminated immediately and any running
goroutines would _die with it_.
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
Makes "ci" a channel on which we can send and receive integers, makes
"cs" a channel for strings and "cf" a channel for types that satisfy
the empty interface. Sending on a channel and receiving from it, is
done with the same operator: "<-".
func main() {
c = make(chan int) <3>
go ready("Tea", 2) <4>
go ready("Coffee", 1) <4>
fmt.Println("I'm waiting, but not too long")
<-c <5>
<-c <5>
}
L: for {
select {
case <-c:
i++
if i > 1 {
break L
}
}
}
While our goroutines were running concurrently, they were not running
in parallel. When you do not tell Go anything there can only be one
goroutine running at a time. With "runtime.GOMAXPROCS(n)" you can
set the number of goroutines that can run in parallel. From the
documentation:
If you do not want to change any source code you can also set an
environment variable "GOMAXPROCS" to the desired value.
Note that the above discussion relates to older versions of Go. From
version 1.5 and above, "GOMAXPROCS" defaults to the number of CPU
cores[go_1_5_release_notes].
When a channel is closed the reading side needs to know this. The
following code will check if a channel is closed.
x, ok = <-ch
Where "ok" is set to "true" the channel is not closed _and_ we've
read something. Otherwise "ok" is set to "false". In that case the
channel was closed and the value received is a zero value of the
channel's type.
7.3. Exercises
7.3.1. Channels
7.3.2. Answer
package main
import "fmt"
func main() {
ch := make(chan int)
go shower(ch)
for i := 0; i < 10; i++ {
ch <- i
}
}
1. An answer is
package main
import "fmt"
func main() {
ch := make(chan int)
quit := make(chan bool)
go shower(ch, quit)
for i := 0; i < 10; i++ {
ch <- i
}
quit <- false // or true, does not matter
}
On line 20 we read from the quit channel and we discard the value we
read. We could have used "q := <-quit", but then we would have used
the variable only once --- which is illegal in Go. Another trick you
might have pulled out of your hat may be: "_ = <-quit". This is
valid in Go, but idomatic Go is the one given on line 20.
7.3.3. Fibonacci II
7.3.4. Answer
package main
import "fmt"
func dup3(in <-chan int) (<-chan int, <-chan int, <-chan int) {
a, b, c := make(chan int, 2), make(chan int, 2), make(chan int, 2)
go func() {
for {
x := <-in
a <- x
b <- x
c <- x
}
}()
return a, b, c
}
func main() {
x := fib()
for i := 0; i < 10; i++ {
fmt.Println(<-x)
}
}
// See sdh33b.blogspot.com/2009/12/fibonacci-in-go.html
8. Communication
Reading from (and writing to) files is easy in Go. This program only
uses the "os" package to read data from the file "/etc/passwd".
package main
import (
"log"
"os"
)
func main() {
buf := make([]byte, 1024)
f, e := os.Open("/etc/passwd") <1>
if e != nil {
log.Fatalf(e)
}
defer f.Close() <2>
for {
n, e := f.Read(buf) <3>
if e != nil {
log.Fatalf(e) <4>
}
if n == 0 { <5>
break
}
os.Stdout.Write(buf[:n]) <6>
}
}
package main
import (
"bufio"
"log"
"os"
)
func main() {
buf := make([]byte, 1024)
f, e := os.Open("/etc/passwd") <1>
if e != nil {
log.Fatalf(e)
}
defer f.Close()
r := bufio.NewReader(f) <2>
w := bufio.NewWriter(os.Stdout)
defer w.Flush() <3>
for {
n, e := r.Read(buf) <4>
if e != nil {
log.Fatalf(e)
}
if n == 0 {
break
}
w.Write(buf[0:n]) <5>
}
}
Again, we open _1_ the file. Then at _2_ we Turn "f" into a buffered
"Reader". "NewReader" expects an "io.Reader", so you this will work.
Then at _4_ we read and at _5_ we write. We also call "Flush()" at
_3_ to flush all output. This entire program could be optimized
further by using "io.Copy".
8.1. io.Reader
If you think of a new type in your program or package and you make it
fulfill the "io.Reader" or "io.Writer" interface, _the whole standard
Go library can be used_ on that type!
The previous program reads a file in its entirety, but a more common
scenario is that you want to read a file on a line-by-line basis.
The following snippet shows a way to do just that (we're discarding
the error returned from "os.Open" here to keep the examples smaller
-- don't ever do this in real life code).
The similarity between these two examples (and with other scripting
languages) have prompted comments that Go has a "script"-like feel to
it, i.e. programming in Go can be compared to programming in an
interpreted language (Python, Ruby, Perl or PHP).
Arguments from the command line are available inside your program via
the string slice "os.Args", provided you have imported the package
"os". The "flag" package has a more sophisticated interface, and
also provides a way to parse flags. Take this example from a DNS
query tool:
After the flags have been parsed you can used them: "if *dnssec { ...
}"
import "os/exec"
The above example just runs "ls -l" without doing anything with the
returned data, capturing the standard output from a command is done
as follows:
And "buf" is byte slice, that you can further use in your program.
8.5. Networking
All network related types and functions can be found in the package
"net". One of the most important functions in there is "Dial". When
you "Dial" into a remote system the function returns a "Conn"
interface type, which can be used to send and receive information.
The function "Dial" neatly abstracts away the network family and
transport. So IPv4 or IPv6, TCP or UDP can all share a common
interface.
Dialing a remote system (port 80) over TCP, then UDP and lastly TCP
over IPv6 looks like this:
If there were no errors (returned in "e"), you can use "conn" to read
and write. And "conn" implements the "io.Reader" and "io.Writer"
interface.
But these are the low level nooks and crannies, you will almost
always use higher level packages, such as the "http" package. For
instance a simple Get for http:
package main
import (
"fmt"
"http"
"io/ioutil"
)
func main() {
r, err := http.Get("http://www.google.com/robots.txt")
if err != nil {
fmt.Printf("%s\n", err.String())
return
}
b, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err == nil {
fmt.Printf("%s", string(b))
}
}
8.6. Exercises
8.6.2. Answer
package main
import (
"bufio"
"errors"
"flag"
"io/ioutil"
"net"
"os/user"
)
func main() {
flag.Parse()
ln, err := net.Listen("tcp", ":79")
if err != nil {
panic(err)
}
for {
conn, err := ln.Accept()
if err != nil {
continue
}
go handleConnection(conn)
}
}
Write a simple echo server. Make it listen to TCP port number 8053
on localhost. It should be able to read a line (up to the newline),
echo back that line and then close the connection.
8.6.4. Answer
package main
import (
"bufio"
"fmt"
"net"
)
func main() {
l, err := net.Listen("tcp", "127.0.0.1:8053")
if err != nil {
fmt.Printf("Failure to listen: %s\n", err.Error())
}
for {
if c, err := l.Accept(); err == nil {
Echo(c)
}
}
}
% nc 127.0.0.1 8053
Go is *awesome*
Go is *awesome*
becomes:
Write a small program that reads text from standard input and
performs the following actions:
In other words implement wc(1) (check you local manual page), however
you only have to read from standard input.
8.6.6. Answer
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
var chars, words, lines int
r := bufio.NewReader(os.Stdin) <1>
for {
switch s, ok := r.ReadString('\n'); true { <2>
case ok != nil: <3>
fmt.Printf("%d %d %d\n", chars, words, lines)
return
default: <4>
chars += len(s)
words += len(strings.Fields(s))
lines++
}
}
}
At _1_ we create a new reader that reads from standard input, we then
read from the input at _2_. And at _3_ we check the value of "ok" and
if we received an error, we assume it was because of a EOF, So we
print the current values;. Otherwise _4_ we count the charaters,
words and increment the number lines.
8.6.7. Uniq
'a' 'b' 'a' 'a' 'a' 'c' 'd' 'e' 'f' 'g'
it should print only those items which don't have the same successor:
#!/usr/bin/perl
my @a = qw/a b a a a c d e f g/;
print my $first = shift @a;
foreach (@a) {
if ($first ne $_) { print; $first = $_; }
}
8.6.8. Answer
package main
import "fmt"
func main() {
list := []string{"a", "b", "a", "a", "c", "d", "e", "f"}
first := list[0]
8.6.9. Quine
8.6.10. Answer
This solution is from Russ Cox. It was posted to the Go Nuts mailing
list.
/* Go quine */
package main
import "fmt"
func main() {
fmt.Printf("%s%c%s%c\n", q, 0x60, q, 0x60)
}
var q = `/* Go quine */
package main
import "fmt"
func main() {
fmt.Printf("%s%c%s%c\n", q, 0x60, q, 0x60)
}
var q = `
8.6.11. Processes
Write a program that takes a list of all running processes and prints
how many child processes each parent has spawned. The output should
look like:
o For acquiring the process list, you'll need to capture the output
of "ps -e -opid,ppid,comm". This output looks like:
o If a parent has one child you must print "child", if there is more
than one print "children".
o The process list must be numerically sorted, so you start with pid
0 and work your way up.
Here is a Perl version to help you on your way (or to create complete
and utter confusion).
#!/usr/bin/perl -l
my (%child, $pid, $parent);
my @ps=`ps -e -opid,ppid,comm`; # capture the output from `ps`
foreach (@ps[1..$#ps]) { # discard the header line
($pid, $parent, undef) = split; # split the line, discard 'comm'
push @{$child{$parent}}, $pid; # save the child PIDs on a list
}
# Walk through the sorted PPIDs
foreach (sort { $a <=> $b } keys %child) {
print "Pid ", $_, " has ", @{$child{$_}}+0, " child",
@{$child{$_}} == 1 ? ": " : "ren: ", "[@{$child{$_}}]";
}
8.6.12. Answer
o Parsing the output and saving the child PIDs for each PPID.
package main
import (
"fmt"
"os/exec"
"sort"
"strconv"
"strings"
)
func main() {
ps := exec.Command("ps", "-e", "-opid,ppid,comm")
output, _ := ps.Output()
child := make(map[int][]int)
for i, s := range strings.Split(string(output), "\n") {
if i == 0 { // kill first line
continue
}
if len(s) == 0 { // kill last line
continue
}
f := strings.Fields(s)
fpp, _ := strconv.Atoi(f[1]) // parent's pid
fp, _ := strconv.Atoi(f[0]) // child's pid
child[fpp] = append(child[fpp], fp)
}
schild := make([]int, len(child))
i := 0
for k, _ := range child {
schild[i] = k
i++
}
sort.Ints(schild)
for _, ppid := range schild {
fmt.Printf("Pid %d has %d child", ppid, len(child[ppid]))
if len(child[ppid]) == 1 {
fmt.Printf(": %v\n", child[ppid])
continue
}
fmt.Printf("ren: %v\n", child[ppid])
}
}
o Pick one (1) random number ("i") in the range: "1 \ldots 1000".
Implement a number cruncher that works like that. Make it print the
solution in a similar format (i.e. output should be infix with
parenthesis) as used above.
Calculate _all_ possible solutions and show them (or only show how
many there are). In the example above there are 544 ways to do it.
8.6.14. Answer
% ./permrec 977
1+(((6+7)*75)+(8/8)) = 977 #1
... ...
((75+(8*6))*8)-7 = 977 #542
(((75+(8*6))*8)-7)*1 = 977 #543
(((75+(8*6))*8)-7)/1 = 977 #544
package main
import (
"flag"
"fmt"
"strconv"
)
const (
_ = 1000 * iota
ADD
SUB
MUL
DIV
MAXPOS = 11
)
var mop = map[int]string{ADD: "+", SUB: "-", MUL: "*", DIV: "/"}
var (
ok bool
value int
)
func main() {
flag.Parse()
list := []int{1, 6, 7, 8, 8, 75, ADD, SUB, MUL, DIV}
magic, ok := strconv.Atoi(flag.Arg(0)) // Arg0 is i
if ok != nil {
return
}
f := make([]int, MAXPOS)
solve(f, list, 0, magic)
}
if index == MAXPOS-1 {
if v < ADD {
numberop[i] = tmp // reset and go on
}
goto NEXT
}
solve(form, numberop, index+1, magic)
if v < ADD {
numberop[i] = tmp // reset and go on
}
NEXT:
}
}
b := stack.Pop()
if t == ADD {
stack.Push(b + a)
}
if t == SUB {
// disallow negative subresults
if b-a < 0 {
return 0, false
}
stack.Push(b - a)
}
if t == MUL {
stack.Push(b * a)
}
if t == DIV {
if a == 0 {
return 0, false
}
// disallow fractions
if b%a != 0 {
return 0, false
}
stack.Push(b / a)
}
default:
stack.Push(t)
}
}
if stack.Len() == 1 { // there is only one!
return stack.Pop(), true
}
return 0, false
}
9. References
[bubblesort]
Wikipedia, "Bubble sort", 2010.
[duck_typing]
Wikipedia, "Duck typing", 2010.
[effective_go]
Authors, G., "Effective Go", 2010.
[fizzbuzz]
Tech, I., "Using fizzbuzz to find developers...", 2010.
[go_1_5_release_notes]
Authors, G., "Go 1.5 Release Notes", 2010.
[go_blog_panic]
Authors, G., "Defer, panic, and recover", 2010.
[go_course_day3]
Pike, R., "The Go programming language, day 3", 2010.
[go_nuts_interfaces]
Community, G., "Function accepting a slice of interface
types", 2010.
[go_tutorial]
Authors, G., "Go tutorial", 2010.
9.2. URIs
[1] https://github.com/miekg/learninggo
[2] https://github.com/mmarkdown/mmark
[3] https://github.com/miekg/gobook
[4] http://creativecommons.org/licenses/by-nc-sa/3.0/
[5] http://www.mikespook.com/learning-go/
[6] https://github.com/mmarkdown/mmark
[7] learninggo-2.txt
[8] http://golang.org/doc/
[9] https://www.debian.org
Index
A
array
multidimensional 21
B
built-in
append 20, 23
cap 20
close 19
complex 20
copy 20, 23
delete 19
imag 20
len 20
make 20, 55
new 20, 55
panic 20
print 20
real 20
recover 20
C
channel
blocking read 85
blocking write 85
unbuffered 85
channels 6, 83
complex numbers 20
D
duck
typing 72
F
field 58
functions
as values 31
exported 44
literal 33
literals 31
method 29
pass-by-value 29
private 44
public 44
receiver 29
signature 32
variadic 34
G
generic 75
goroutine 6, 82
I
interface 71
set of methods 71
type 71
value 71
io
buffered 91
io.Reader 91
K
keywords
break 16
continue 17
default 19
defer 32
defer list 33
else 15
fallthrough 19
for 16
go 82
goto 16
if 15
import 45
iota 12
map 23
map adding elements 24
map existence 24
map remove elements 24
package 44
range 17-18, 24
return 15
struct 57
switch 18
type 57
label 16
literal
composite 21, 56
M
methods
inherited 60
N
networking
Dial 93
nil 54
O
operators
address-of 54
and 14
bit wise xor 14
bitwise and 14
bitwise clear 14
bitwise or 14
channel 83
increment 55
not 14
or 14
P
package 44
bufio 46, 49, 91
builtin 19
bytes 45
encoding/json 50
flag 50, 92
fmt 20, 49
html/template 50
io 49, 91
net/http 50
os 50
os/exec 51, 93
reflect 50, 77
ring 46
sort 50
strconv 50
sync 50
unsafe 50
R
reference types 21
runes 13, 18
S
scope
local 30
slice
capacity 21
length 21
structures
embed 60
T
tooling
go 10
go build 10
go run 10
go test 47
type assertion 73
type switch 72
V
variables
assigning 10
declaring 10
parallel assignment 11
underscore 11
Author's Address
R. (Miek) Gieben
Email: [email protected]