Learning Go
Learning Go
Preface
The source of this book is written in mmark and is converted from the original
LaTeX source.
All example code used in this book is hereby licensed under the Apache License
version 2.0.
The following people made large or small contributions to earlier versions of this
book:
“Learning Go” has been translated into (note that this used the original LaTeX
source).
https://www.miek.nl/go/ 1/85
12/28/24, 2:08 PM Learning Go
This book still sees development, small incremental improvements trickle in from
Github.
Learning Go’s source has been rewritten in mmark2, but did not see any other
changes. This books translates cleanly into an RFC-like document.
LEARNING
GO
https://www.miek.nl/go/ 2/85
12/28/24, 2:08 PM Learning Go
Chapter 1. Introduction
[go_web]
According to the website “Go is expressive, concise, clean, and efficient”.
And indeed it is. My initial interest was piqued when I read early announcements
about this new language that had built-in concurreny and a C-like syntax (Erlang
also has built-in concurrency, but I could never get used to its syntax). Go is a
compiled statically typed language that feels like a dynamically typed, interpreted
language. My go to (scripting!) language Perl has taken a back seat now that Go
is around.
https://www.miek.nl/go/ 3/85
12/28/24, 2:08 PM Learning Go
https://www.miek.nl/go/ 4/85
12/28/24, 2:08 PM Learning Go
The best way to learn Go is to create your own programs. Each chapter therefore
includes exercises (and answers to exercises) to acquaint you with the language.
Each exercise is either easy, intermediate, or difficult. The answers are included
after the exercises on a new page. Some exercises don’t have an answer; these
are marked with an asterisk.
Chapter 2. Basics
We’ll look at the basic types, variables, and control structures available in
the language.
Chapter 3. Functions
Here we look at functions, the basic building blocks of Go programs.
Chapter 4. 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.
Chapter 5. Beyond the basics
We’ll create our own types. We’ll also look at memory allocations in Go.
Chapter 6. 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.
Chapter 7. 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.
Chapter 8. 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.
Official Documentation
There is a substantial amount of documentation written about Go. The Go Tutorial
[go_tutorial] [effective_go]
, the Go Tour (with lots of exercises) and the Effective Go are
helpful resources. The website http://golang.org/doc/ is a very good starting point
for reading up on Go3. Reading these documents is certainly not required, but it is
recommended.
When searching on the internet use the term “golang” instead of plain “go”.
https://www.miek.nl/go/ 5/85
12/28/24, 2:08 PM Learning Go
Go comes with its own documentation in the form of a program called godoc4. If
you are interested in the documentation for the built-ins, simply do this:
% godoc builtin
% godoc hash
To read the documentation of fnv contained in hash, you’ll need to issue godoc
hash/fnv as fnv is a subdirectory of hash.
PACKAGE DOCUMENTATION
package fnv
import "hash/fnv"
Chapter 2. Basics
In this chapter we will look at the basic building blocks of the Go programming
language.
Hello World
https://www.miek.nl/go/ 6/85
12/28/24, 2:08 PM Learning Go
In the Go tutorial, you get started with Go in the typical manner: printing “Hello
World” (Ken Thompson and Dennis Ritchie started this when they presented the C
language in the 1970s). That’s a great way to start, so here it is, “Hello World” in
Go.
package main 1
/* Print something */ 3
func main() { 4
fmt.Printf("Hello, world.") 5
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.
import "fmt" says we need fmt in addition to main . A package other than main
2
Finally we call a function from the package fmt to print a string to the screen. The
string is enclosed with " and may contain non-ASCII characters 5 .
% go build helloworld.go
% ./helloworld
Hello, world.
You can combine the above and just call go run helloworld.go.
https://www.miek.nl/go/ 7/85
12/28/24, 2:08 PM Learning Go
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 Constants) and
import also allow this. Note the use of parentheses instead of braces:
var (
x int
b bool
)
Multiple variables of the same type can also be declared on a single line: var x,
y int makes x and y both int variables. You can also make use of parallel
assignment a, b := 20, 16. This makes a and b both integer variables and
assigns 20 to a and 16 to b.
A special name for a variable is _. Any value assigned to it is discarded (it’s similar
to /dev/null on Unix). In this example we only assign the integer value of 35 to b
and discard the value 34: _, b := 34, 35. Declared but otherwise unused
variables are a compiler error in Go.
Boolean Types
https://www.miek.nl/go/ 8/85
12/28/24, 2:08 PM Learning Go
A boolean type represents the set of boolean truth values denoted by the
predeclared constants true and false. The boolean type is bool.
Numerical Types
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
}
Constants
Constants in Go are just that — constant. They are created at compile time, and
can only be numbers, strings, or booleans; const x = 42 makes x a constant.
You can use iota 5 to enumerate values.
const (
a = iota
https://www.miek.nl/go/ 9/85
12/28/24, 2:08 PM Learning Go
b
)
The first use of iota will yield 0, so a is equal to 0. Whenever iota is used again
on a new line its value is incremented with 1, so b has a value of 1. Or, as shown
here, you can even let Go repeat the use of iota. You may also explicitly type a
constant: const b string = "0". Now b is a string type constant.
Strings
s := "Hello World!"
s := "hello"
c := []rune(s) 1
c[0] = 'c' 2
s2 := string(c) 3
fmt.Printf("%s\n", s2) 4
Runes
Rune is an alias for int32. It is an UTF-8 encoded code point. When is this type
useful? One example is when you’re iterating over characters in a string. You
could loop over each byte (which is only equivalent to a character when strings
https://www.miek.nl/go/ 10/85
12/28/24, 2:08 PM Learning Go
are encoded in 8-bit ASCII, which they are not in Go!). But to get the actual
characters you should use the rune type.
Complex Numbers
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 + imi , where re is the real
−−
−
part, im is the imaginary part and i is the literal ‘i ’ (√−1 ).
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 Chapter 6. Interfaces. For now you can
just assume that error is a type just like all other types.
Precedence Operator(s)
`+ -
<-
&&
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 !
Although Go does not support operator overloading (or method overloading for
that matter), some of the built-in operators are overloaded. For instance, + can be
https://www.miek.nl/go/ 11/85
12/28/24, 2:08 PM Learning Go
used for integers, floats, complex numbers and strings (adding strings is
concatenating them).
Go Keywords
Let’s start looking at keywords, Table 2 lists all the keywords in Go.
We’ve seen some of these already. We used var and const in the Variables,
Types and Keywords 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:
Control Structures
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 Channels). Parentheses are not required around the
condition, and the body must always be brace-delimited.
If-Else
https://www.miek.nl/go/ 12/85
12/28/24, 2:08 PM Learning Go
if x > 0 {
return y
} else {
return x
}
Since if and switch accept an initialization statement, it’s common to see one
used to set up a (local) variable.
It is idomatic in Go to omit the else when the if statement’s body has a break,
continue, return or, goto, so the above code would be better written as:
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.
Goto
Go has a goto statement - use it wisely. With goto you jump to a label which must
be defined within the current function. For instance, a loop in disguise:
func myfunc() {
i := 0
Here:
fmt.Println(i)
i++
goto Here
}
https://www.miek.nl/go/ 13/85
12/28/24, 2:08 PM Learning Go
The string Here: indicates a label. A label does not need to start with a capital
letter and is case sensitive.
For
The Go for loop has three forms, only one of which has semicolons:
for init; condition; post { } - a loop using the syntax borrowed from
C;
for condition { } - a while loop, and;
for { } - an endless loop.
Short declarations make it easy to declare the index variable right in the loop.
sum := 0
for i := 0; i < 10; i++ {
sum = sum + i
}
With break you can quit loops early. By itself, break breaks the current loop.
}
fmt.Println(i) 2
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:
}
fmt.Println(i)
https://www.miek.nl/go/ 14/85
12/28/24, 2:08 PM Learning Go
}
}
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.
Range
The keyword range can be used for loops. It can loop over slices, arrays, strings,
maps and channels (see Channels). 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:
First we create a slice of strings. Then we use range to loop over them. With each
iteration, range will return the index as an int 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
https://www.miek.nl/go/ 15/85
12/28/24, 2:08 PM Learning Go
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.
return c - '0' 3
case 'a' <= c && c <= 'f': 4
return c - 'a' + 10
case 'A' <= c && c <= 'F': 5
return c - 'A' + 10
}
return 0
A switch without a condition is the same as switch true 1 . We list the different
cases. Each case statement has a condition that is either true of false. Here 2 we
check if c is a number. If c is a number we return its value 3 . Check if c falls
between “a” and “f” 4 . For an “a” we return 10, for “b” we return 11, etc. We also
do the same 5 thing for “A” to “F”.
There is no automatic fall through, you can use fallthrough for that.
switch i {
case 0: fallthrough
case 1: 1
f()
default:
g() 2
f() can be called when i == 0 . With default you can specify an action when
1
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:
https://www.miek.nl/go/ 16/85
12/28/24, 2:08 PM Learning Go
switch i {
case 0, 1: 1
f()
default:
g()
Built-in Functions
A few functions are predefined, meaning you don’t have to include any package to
get access to them. Table 3 lists them all.6
These built-in functions are documented in the builtin pseudo package that is
included in recent Go releases. Let’s go over these functions briefly.
close
is used in channel communication. It closes a channel. We’ll learn more
about this in Channels.
delete
is used for deleting entries in maps.
len and cap
are used on a number of different types, len is used to return the lengths of
strings, maps, slices, and arrays. In the next section Arrays we’ll look at
slices, arrays and the function cap.
new
is used for allocating memory for user defined data types. See Allocation
with new.
make
is used for allocating memory for built-in types (maps, slices, and channels).
See Allocation with make.
copy, append
https://www.miek.nl/go/ 17/85
12/28/24, 2:08 PM Learning Go
copy is for copying slices. And append is for concatenating slices. See Slices
in this chapter.
panic, recover
are used for an exception mechanism. See Panic and recovering 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)
complex, real, imag
all deal with complex numbers. We will not use complex numbers in this
book.
Arrays
An array is defined by: [n]<type>, where n is the length of the array and <type>
is the stuff you want to store. To assign or index an element in the array, you use
square brackets:
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 different type. Also arrays are
values: Assigning one array to another copies all the elements. In particular, if you
pass an array to a function it will receive a copy of the array, not a pointer to it.
To declare an array you can use the following: var a [3]int. To initialize it to
something other than zero, use a composite literal a := [3]int{1, 2, 3}. This
can be shortened to a := [...]int{1, 2, 3}, where Go counts the elements
automatically.
https://www.miek.nl/go/ 18/85
12/28/24, 2:08 PM Learning Go
When declaring arrays you always have to type something in between the square
brackets, either a number or three dots (...), when using a composite literal.
When using multidimensional arrays, you can use the following syntax: a := [2]
[2]int{ {1,2}, {3,4} }. Now that you know about arrays you will be delighted
to learn that you will almost never use them in Go, because there is something
much more flexible: slices.
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.
Reference types are created with make. We detail this further in Chapter 5.
Beyond the basics.
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 .
Next, we create a slice from this array: slice := array[:n] . And now we have:
len(slice) == n
cap(slice) == m
len(array) == cap(array) == m
https://www.miek.nl/go/ 19/85
12/28/24, 2:08 PM Learning Go
Given an array, or another slice, a new slice is created via a[n:m]. This creates a
new slice which refers to the variable a, starts at index n, and ends before index m.
It has length n - 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
First we define 1 an array with five elements, from index 0 to 4. From this we
create 2 a slice with the elements from index 2 to 3, this slices contains: 3, 4.
Then we we create another slice 3 from a: with the elements from index 1 to 4,
this contains: 2, 3, 4, 5. With a[:] 4 we create a slice with all the elements in
the array. This is a shorthand for: a[0:len(a)]. And with a[:4] 5 we create a
slice with the elements from index 0 to 3, this is short for: a[0:4], and gives us a
slices that contains: 1, 2, 3, 4. With s2[:] we create a slice from the slice s2 6 ,
note that s5 still refers to the array a. Finally, we create a slice with the elements
from index 3 to 3 and also set the cap to 4 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
https://www.miek.nl/go/ 20/85
12/28/24, 2:08 PM Learning Go
slice[98] = 1 3
slice[99] = 2 4
If you want to extend a slice, there are a couple of built-in functions that make life
easier: append and copy. The append function appends zero or more values to a
slice and returns the result: a slice with the same type as the original. If the
original slice isn’t big enough to fit the added values, append will allocate a new
slice that is big enough. So the slice returned by append may refer to a different
underlying array than the original slice does. Here’s an example:
s0 := []int{0, 0}
s1 := append(s0, 2) 1
s2 := append(s1, 3, 5, 7) 2
s3 := append(s2, s0...) 3
Note the three dots used after s0...! This is needed make it clear explicit that
you’re appending another slice, instead of a single value.
The copy function copies slice elements from a source to a destination, and
returns the number of elements it copied. This number is the minimum of the
length of the source and the length of the destination. For example:
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
Maps
https://www.miek.nl/go/ 21/85
12/28/24, 2:08 PM Learning Go
Many other languages have a type similar to maps built-in. For instance, Perl has
hashes, Python has its dictionaries, and C++ also has maps (as part of the
libraries). In Go we have the map type. A map can be thought of as an array
indexed by strings (in its most simple form).
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
The general syntax for defining a map is map[<from type>]<to type>. Here, we
define a map that converts from a string (month abbreviation) to an int (number
of days in that month). Note that the trailing comma at 1 is required.
For indexing (“searching”) the map, we use square brackets. For example,
suppose we want to print the number of days in December: fmt.Printf("%d\n",
monthdays["Dec"])
If you are looping over an array, slice, string, or map a, range clause will help you
again, it returns the key and corresponding value with each invocation.
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.
https://www.miek.nl/go/ 22/85
12/28/24, 2:08 PM Learning Go
You can remove elements from the map: delete(monthdays, "Mar") 7. In general
the syntax delete(m, x) will delete the map entry retrieved by the expression
m[x].
Exercises
For-loop
1. Create a loop with the for construct. Make it loop 10 times and print out the
loop counter with the fmt package.
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.
▷ Answer
Average
▷ Answer
FizzBuzz
[fizzbuzz]FizzBuzz
1. Solve this problem, called the Fizz-Buzz problem:
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”.
▷ Answer
Chapter 3. Functions
https://www.miek.nl/go/ 23/85
12/28/24, 2:08 PM Learning Go
Richard P. Gabriel
Functions are the basic building blocks of Go programs; all interesting stuff
happens in them.
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 Chapter 6. Interfaces. 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.
As said the return or result parameters of a Go function can be given names and
used as regular variables, just like the incoming parameters. When named, they
are initialized to the zero values for their types when the function begins. If the
function executes a return statement with no arguments, the current values of the
https://www.miek.nl/go/ 24/85
12/28/24, 2:08 PM Learning Go
result parameters are returned. Using these features enables you (again) to do
more with less code.8
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 Functions as values 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).
Scope
Variables declared outside any functions are global in Go, those defined in
functions are local to those functions. If names overlap - a local variable is
declared with the same name as a global one - the local variable hides the global
one when the current function is executed.
package main
var a int 1
func main() {
a = 5
print(a)
f()
https://www.miek.nl/go/ 25/85
12/28/24, 2:08 PM Learning Go
func f() {
a := 6 2
print(a)
g()
}
func g() {
print(a)
}
Here 1 , we declare a to be a global variable of type int. Then in the main function
we give the global a the value of 5, after printing it we call the function f. Then
here 2 , a := 6, we create a new, local variable also called a. This new a gets the
value of 6, which we then print. Then we call g, which uses the global a again and
prints a’s value set in main. Thus 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.
Functions as values
As with almost everything in Go, functions are also just values. They can be
assigned to variables as follows:
import "fmt"
func main() {
a := func() { 1
fmt.Println("Hello")
} 2
a() 3
https://www.miek.nl/go/ 26/85
12/28/24, 2:08 PM Learning Go
Or you can write a function that takes a function as its parameter, for example a
Map function that works on int slices. This is left as an exercise for the reader; see
the exercise Map function.
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)
Deferred Code
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
https://www.miek.nl/go/ 27/85
12/28/24, 2:08 PM Learning Go
early. If you do that, you will need to close the file descriptor you are working on.
This often leads to the following code:
return false
}
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.
}
if failureY {
return false 2
}
return true 2
https://www.miek.nl/go/ 28/85
12/28/24, 2:08 PM Learning Go
You can put multiple functions on the “defer list”, like this example from
Deferred functions are executed in LIFO order, so the above code prints: 4 3 2 1
0.
With defer you can even change return values, provided that you are using
named result parameters and a function literal 9, 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 parentheses
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 parentheses:
In this (unnamed) function you can access any named return parameter:
ret++
}()
return 0
}
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!
Variadic Parameter
Functions that take a variable number of parameters are known as variadic
functions. To declare a function as variadic, do something like this:
https://www.miek.nl/go/ 29/85
12/28/24, 2:08 PM Learning Go
The arg ...int instructs Go to see this as a function that takes a variable
number of arguments. Note that these arguments all have to have the type int. In
the body of your function the variable arg is a slice of ints:
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.
If you don’t specify the type of the variadic argument it defaults to the empty
interface interface{} (see Chapter Chapter 6. Interfaces).
Suppose we have another variadic function called myfunc2, the following example
shows how to pass variadic arguments to it:
With myfunc2(arg...) we pass all the parameters to myfunc2, but because the
variadic parameters is just a slice, we can use some slice tricks as well.
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
https://www.miek.nl/go/ 30/85
12/28/24, 2:08 PM Learning Go
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.
This function checks if the function it gets as argument will panic when it is
executed10:
Execute the function we received as the argument. And finally 4 we return the
value of b. Because b is a named return parameter.
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))
}
https://www.miek.nl/go/ 31/85
12/28/24, 2:08 PM Learning Go
On line 3 the a[3] = 5 triggers a runtime out of bounds error which results in a
panic. Thus this program will print true. If we change line 2: var a []int to var
a [4]int the function panicy does not panic anymore. Why?
Exercises
Average
▷ Answer
Bubble sort
[bubblesort]
1. Write a function that performs a bubble sort on a slice of ints. From :
▷ Answer
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.
▷ Answer
https://www.miek.nl/go/ 32/85
12/28/24, 2:08 PM Learning Go
Fibonacci
Write a function that takes an int value and gives that many terms of the
Fibonacci sequence.
▷ Answer
Var args
1. Write a function that takes a variable number of ints and print each integer
on a separate line.
▷ Answer
p := plusTwo()
fmt.Printf("%v\n", p(2))
2. Generalize the function from above and create a plusX(x) which returns
functions that add x to an integer.
▷ Answer
Maximum
1. Write a function that finds the maximum value in an int slice ([]int).
▷ Answer
Map function
https://www.miek.nl/go/ 33/85
12/28/24, 2:08 PM Learning Go
▷ Answer
Stack
1. Create a simple stack which can hold a fixed number of ints. It does not
have to grow beyond this limit. Define push – put something on the stack –
and pop – retrieve something from the stack – functions. The stack should
be a LIFO (last in, first out) stack.
Figure 2. A stack.
▷ Answer
Chapter 4. Packages
“^”
https://www.miek.nl/go/ 34/85
12/28/24, 2:08 PM Learning Go
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.
package even 1
return i%2 == 0
}
return i%2 == 1
}
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.
Now we just need to build the package. We create a directory under $GOPATH, and
copy even.go there (see Compiling and Running Code in Chapter 2. Basics).
% 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
https://www.miek.nl/go/ 35/85
12/28/24, 2:08 PM Learning Go
Import 1 the following packages. The local package even is imported here 2 . This
3 imports the official fmt package. And 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
Note that the “starts with capital → exported”, “starts with lower-case → private”
rule also extends to other names (new types, global variables) defined in the
package. Note that the term “capital” is not limited to US-ASCII – it extends to all
bicameral alphabets (Latin, Greek, Cyrillic, Armenian and Coptic).
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.
As we did above in our myeven program, accessing content from an imported (with
import ) package is done with using the package’s name and then a dot. After
import "bytes" the importing program can talk about bytes.Buffer. A package
name should be good, short, concise and evocative. The convention in Go is that
package names are lowercase, single 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
https://www.miek.nl/go/ 36/85
12/28/24, 2:08 PM Learning Go
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.
It is important to avoid stuttering when naming things. For instance, the buffered
reader type in the bufio package is called Reader, not BufReader, because users
see it as bufio.Reader, which is a clear, concise name.
Documenting packages
When we created our even package, we skipped over an important item:
documentation. Each package should have a package comment, a block
comment preceding the package clause. In our case we should extend the
beginning of the package, with:
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.
https://www.miek.nl/go/ 37/85
12/28/24, 2:08 PM Learning Go
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.
Testing packages
In Go it is customary to write (unit) tests for your package. Writing tests involves
the testing package and the program go test. Both have excellent
documentation.
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):
https://www.miek.nl/go/ 38/85
12/28/24, 2:08 PM Learning Go
func (t *T) Fail(), Fail marks the test function as having failed but
continues execution.
func (t *T) FailNow(), FailNow marks the test function as having failed
and stops its execution. Any remaining tests in this file are skipped, and
execution continues with the next test.
Putting all this together we can write our test. First we pick a name:
even_test.go. Then we add the following contents:
package even 1
import "testing" 2
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:
if Even(2) {
t.Log("2 should be odd!")
t.Fail()
}
}
We now get:
And you can act accordingly (by fixing the test for instance).
Writing new packages should go hand in hand with writing (some) documentation
and test functions. It will make your code better and it shows that you really put in
the effort.
The Go test suite also allows you to incorporate example functions which serve as
documentation and as tests. These functions need to start with Example.
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.
Useful packages
The standard libary of Go includes a huge number of packages. It is very
enlightening to browse the $GOROOT/src directory and look at the packages. We
cannot comment on each package, but the following are worth a mention: 11
fmt
https://www.miek.nl/go/ 40/85
12/28/24, 2:08 PM Learning Go
%v, the value in a default format. when printing structs, the plus flag
(%+v) adds field names.
%#v, a Go-syntax representation of the value.
%T, a Go-syntax representation of the type of the value.
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
[RFC4627]
objects as defined in RFC 4627 .
https://www.miek.nl/go/ 41/85
12/28/24, 2:08 PM Learning Go
html/template
Data-driven templates for generating textual output such as HTML.
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 object with interface type Type. See
Chapter 6. Interfaces, Section Introspection and reflection.
os/exec
The os/exec package runs external commands.
Exercises
Stack as package
2. Write a simple unit test for this package. You should at least test that a Pop
works after a Push.
▷ Answer
Calculator
https://www.miek.nl/go/ 42/85
12/28/24, 2:08 PM Learning Go
▷ Answer
Go has pointers. There is however no pointer arithmetic, so they act more like
references than pointers that you may know from C. Pointers are useful.
Remember that when you call a function in Go, the variables are pass-by-value.
So, for efficiency and the possibility to modify a passed value in functions we have
pointers.
You declare a pointer by prefixing the type with an ‘*’: var p *int. Now p is a
pointer to an integer value. All newly declared variables are assigned their zero
value and pointers are no different. A newly declared pointer, or just a pointer that
points to nothing, has a nil-value . In other languages this is often called a NULL
pointer in Go it is just nil. To make a pointer point to something you can use the
address-of operator (&), which we demonstrate here:
var p *int
fmt.Printf("%v", p) 1
https://www.miek.nl/go/ 43/85
12/28/24, 2:08 PM Learning Go
var i int 2
p = &i 3
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 *.
Allocation
Go also has garbage collection, meaning that you don’t have to worry about
memory deallocation.12
To allocate memory Go has two primitives, new and make. They do different things
and apply to different types, which can be confusing, but the rules are simple. The
following sections show how to handle allocation in Go and hopefully clarifies the
somewhat artificial distinction between new and make .
The built-in function new is essentially the same as its namesakes in other
languages: new(T) allocates zeroed storage for a new item of type T and returns
its address, a value of type *T. Or in other words, it returns a pointer to a newly
allocated zero value of type T. This is important to remember.
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.
The built-in function make(T, args) serves a purpose different from new(T). It
creates slices, maps, and channels only, and it returns an initialized (not zero!)
value of type T, and not a pointer: *T. The reason for the distinction is that these
three types are, under the covers, references to data structures that must be
initialized before use. A slice, for example, is a three-item descriptor containing a
pointer to the data (inside an array), the length, and the capacity; until those items
https://www.miek.nl/go/ 44/85
12/28/24, 2:08 PM Learning Go
are initialized, the slice is nil. For slices, maps, and channels, make initializes the
internal data structure and prepares the value for use.
For instance, make([]int, 10, 100) allocates an array of 100 ints and then
creates a slice structure with length 10 and a capacity of 100 pointing at the first
10 elements of the array. In contrast, new([]int) returns a pointer to a newly
allocated, zeroed slice structure, that is, a pointer to a nil slice value. These
examples illustrate the difference between new and make.
v := make([]int, 100) 4
Allocates 1 slice structure; rarely useful. v 2 refers to a new array of 100 ints. At 3
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.
Sometimes the zero value isn’t good enough and an initializing constructor is
necessary, as in this example taken from the package os.
https://www.miek.nl/go/ 45/85
12/28/24, 2:08 PM Learning Go
f := new(File)
f.fd = fd
f.name = name
f.dirinfo = nil
f.nepipe = 0
return f
}
There’s a lot of boiler plate in there. We can simplify it using a composite literal ,
which is an expression that creates a new instance each time it is evaluated.
It is OK to return the address of a local variable 1 the storage associated with the
variable survives after the function returns.
In fact, taking the address of a composite literal allocates a fresh instance each
time it is evaluated, so we can combine these last two lines.13
The items (called fields) of a composite literal are laid out in order and must all be
present. However, by labeling the elements explicitly as field:value pairs, the
initializers can appear in any order, with the missing ones left as their respective
zero values. Thus we could say
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
https://www.miek.nl/go/ 46/85
12/28/24, 2:08 PM Learning Go
initializations work regardless of the values of Enone, and Einval, as long as they
are distinct:
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)
}
That is nice! Go knows how to print your structure. If you only want to print one, or
a few, fields of the structure you’ll need to use .<field name>. For example to
only print the name:
fmt.Printf("%s", a.name)
https://www.miek.nl/go/ 47/85
12/28/24, 2:08 PM Learning Go
As said each item in a structure is called a field. A struct with no fields: struct {}.
Or one with four fields:
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
Chapter 4. Packages for the details.
Methods
If you create functions that work on your newly defined type, you can take two
routes:
https://www.miek.nl/go/ 48/85
12/28/24, 2:08 PM Learning Go
var n *NameAge
n.doSomething(2)
[go_spec]
But keep the following in mind, this is quoted from :
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).
There is a subtle but major difference between the following type declarations.
[go_spec]
Also see the Section “Type Declarations” . Suppose we have:
NewMutex is equal to Mutex, but it does not have any of the methods of Mutex. In
other words its method set is empty. But PrintableMutex has inherited the
[go_spec]
method set from Mutex. The Go term for this is embedding . In the words of :
The method set of *PrintableMutex contains the methods Lock and Unlock
bound to its anonymous field Mutex.
https://www.miek.nl/go/ 49/85
12/28/24, 2:08 PM Learning Go
Conversions
Sometimes you want to convert a type to another type. This is possible in Go, but
there are some rules. For starters, converting from one value to another is done
by operators (that look like functions: byte()) and not all conversions are allowed.
f
From b []byte i []int r []rune s string
floa
To
[]byte · []byte(s)
[]int · []int(s)
[]rune []rune(s)
float32 ·
int int(
Converts to a byte slice, each byte contains the integer value of the
corresponding byte in the string. Note that as strings in Go are encoded in
UTF-8 some characters in the string may end up in 1, 2, 3 or 4 bytes.
runeslice := []rune(mystring)
Converts to an rune slice, each rune contains a Unicode code point. Every
character from the string corresponds to one rune.
https://www.miek.nl/go/ 50/85
12/28/24, 2:08 PM Learning Go
i := []rune{257,1024,65}
r := string(i)
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
Note that converting structures that are not identical in their fields is more difficult.
Also note that converting b to a plain int also fails; an integer is not the same as a
structure containing an integer.
Exercises
Map function with interfaces
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.
▷ Answer
Pointers
https://www.miek.nl/go/ 51/85
12/28/24, 2:08 PM Learning Go
var p1 Person
p2 := new(Person)
and
func Set(t T) {
x= &t
}
▷ Answer
Linked List
2. Create your own linked list implementation. And perform the same actions as
above.
▷ Answer
Cat
https://www.miek.nl/go/ 52/85
12/28/24, 2:08 PM Learning Go
3. The solution to the above question given in contains a bug. Can you spot
and fix it?
▷ Answer
Method calls
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)
}
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:
So the receiver has to be of type *IntVector, why does the code above (the
Push statements) work correctly then?
▷ Answer
https://www.miek.nl/go/ 53/85
12/28/24, 2:08 PM Learning Go
Chapter 6. Interfaces
In Go, the word interface is overloaded to mean several different things. Every
type has an interface, which is the set of methods defined for that type. This bit of
code defines a struct type S with one field, and defines two methods for S. 15
You can also define an interface type, which is simply a set of methods. This
defines an interface I with two methods:
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:
https://www.miek.nl/go/ 54/85
12/28/24, 2:08 PM Learning Go
func f(p I) { 1
fmt.Println(p.Get()) 2
p.Put(1) 3
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
[duck_typing]
interface means that Go implements a form of 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.
Which is what?
Let’s define another type R that also implements the interface I:
https://www.miek.nl/go/ 55/85
12/28/24, 2:08 PM Learning Go
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) .
Empty interface
Since every type satisfies the empty interface: interface{} we can create a
generic function which has an empty interface as its argument:
The return something.(I).Get() is the tricky bit in this function. The value
something has type interface{}, meaning no guarantee of any methods at all: it
could contain any type. The .(I) is a type assertion which converts something to
an interface of type I. If we have that type we can invoke the Get() function. So if
https://www.miek.nl/go/ 56/85
12/28/24, 2:08 PM Learning Go
we create a new variable of the type *S, we can just call g(), because *S also
implements the empty interface.
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.
Methods
Methods are functions that have a receiver (see Chapter 3. Functions). 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:
Doing this on non-local (types defined in other packages) types yields an error
“cannot define new methods on non-local type int”.
https://www.miek.nl/go/ 57/85
12/28/24, 2:08 PM Learning Go
receiver can not be an interface type, doing so results in a “invalid receiver type
[go_spec]
…” compiler error. The authoritative word from the language spec :
Interface names
By convention, one-method interfaces are named by the method name plus the -
er suffix: Reader, Writer, Formatter etc.
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. 16
A sorting example
Recall the Bubblesort exercise, where we sorted an array of integers:
https://www.miek.nl/go/ 58/85
12/28/24, 2:08 PM Learning Go
}
}
A version that sorts strings is identical except for the signature of the function:
func bubblesortString(n []string) { /* ... */ } . Using this approach
would lead to two functions, one for each type. By using interfaces we can make
this more generic. Let’s create a new function that will sort both strings and
integers, something along the lines of this non-working example:
switch i.(type) { 2
case string: 3
// ...
case int:
// ...
}
return /* ... */ 4
Our function will receive a slice of empty interfaces at 1 . We then 2 use a type
switch to find out what the actual type of the input is. And then 3 then sort
accordingly. And, when done, return 4 the sorted slice.
But when we call this function with sort([]int{1, 4, 5}), it fails with: “cannot
use i (type []int) as type []interface { } in function argument”
This is because Go can not easily convert to a slice of interfaces. Just converting
to an interface is easy, but to a slice is much more costly. The full mailing list
[go_nuts_interfaces]
discussion on this subject can be found at . To keep a long story
short: Go does not (implicitly) convert slices for you.
So what is the Go way of creating such a “generic” function? Instead of doing the
type inference ourselves with a type switch, we let Go do it implicitly: The following
steps are required:
https://www.miek.nl/go/ 59/85
12/28/24, 2:08 PM Learning Go
Define new types for the slices we want to sort. Note that we declare slice
types:
type Xi []int
type Xs []string
At 1 x is now of the Sorter type and using the defined methods for this
interface we implement Bubblesort at 2 .
https://www.miek.nl/go/ 60/85
12/28/24, 2:08 PM Learning Go
Sort(ints)
fmt.Printf("%v\n", ints)
Sort(strings)
fmt.Printf("%v\n", strings)
Here another interface is listed inside the definition of heap.Interface, this may
look odd, but is perfectly valid, remember that on the surface an interface is
nothing more than a listing of methods. sort.Interface is also such a listing, so it
is perfectly legal to include it in the interface.
https://www.miek.nl/go/ 61/85
12/28/24, 2:08 PM Learning Go
Elem returns a type’s element type. It panics if the type’s Kind is not Array,
Chan, Map, Ptr, or Slice.
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 0th 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:
v := reflect.ValueOf(i) 2
tag := t.Elem().Field(0).Tag 3
name := v.Elem().Field(0).String() 4
}
}
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 Elem() on
v to do the redirection. we have “arrived” at the structure. Then we go to the first
field Field(0) and invoke the String() method on it.
https://www.miek.nl/go/ 62/85
12/28/24, 2:08 PM Learning Go
Setting a value works similarly as getting a value, but only works on exported
members. Again some code:
https://www.miek.nl/go/ 63/85
12/28/24, 2:08 PM Learning Go
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.
Exercises
### Interfaces and max()
▷ Answer
One of the last paragraphs in section Introspection and reflection 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.
▷ Answer
Chapter 7. Concurrency
In this chapter we will show off Go’s ability for concurrent programming using
channels and goroutines. Goroutines are the central entity in Go’s ability for
concurrency.
[effective_go]
But what is a goroutine, from :
A goroutine is a normal function, except that you start it with the keyword go.
func main() {
go ready("Tea", 2) //<1>
go ready("Coffee", 1) //<1>
fmt.Println("I'm waiting")
time.Sleep(5 * time.Second) //<2>
https://www.miek.nl/go/ 65/85
12/28/24, 2:08 PM Learning Go
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.
To fix this we need some kind of mechanism which allows us to communicate with
the goroutines. This mechanism is available to us in the form of channels . A
channel can be compared to a two-way pipe in Unix shells: you can send to and
receive values from it. Those values can only be of a specific type: the type of the
channel. If we define a channel, we must also define the type of the values we can
send on the channel. Note that we must use make to create a channel:
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
func main() {
c = make(chan int) 3
go ready("Tea", 2) 4
go ready("Coffee", 1) 4
https://www.miek.nl/go/ 66/85
12/28/24, 2:08 PM Learning Go
There is still some remaining ugliness; we have to read twice from the channel 5 ).
This is OK in this case, but what if we don’t know how many goroutines we
started? This is where another Go built-in comes in: select (((keywords, select))).
With select you can (among other things) listen for incoming data on a channel.
Using select in our program does not really make it shorter, because we run too
few go-routines. We remove last lines and replace them with the following:
L: for {
select {
case <-c:
i++
if i > 1 {
break L
}
}
}
We will now wait as long as it takes. Only when we have received more than one
reply on the channel c will we exit the loop L.
https://www.miek.nl/go/ 67/85
12/28/24, 2:08 PM Learning Go
current setting. This call will go away when the scheduler improves.
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
[go_1_5_release_notes]
and above, GOMAXPROCS defaults to the number of CPU cores .
More on channels
When you create a channel in Go with ch := make(chan bool), an unbuffered
channel for bools is created. What does this mean for your program? For one, if
you read (value := <-ch) it will block until there is data to receive. Secondly
anything sending (ch <- true) will block until there is somebody to read it.
Unbuffered channels make a perfect tool for synchronizing multiple goroutines.
But Go allows you to specify the buffer size of a channel, which is quite simply
how many elements a channel can hold. ch := make(chan bool, 4), creates a
buffered channel of bools that can hold 4 elements. The first 4 elements in this
channel are written without any blocking. When you write the 5th element, your
code will block, until another goroutine reads some elements from the channel to
make room.
value == 0 → unbuffered
ch := make(chan type, value) {
value > 0 → buffer value elements
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.
Exercises
Channels
communication should happen via channels. You should not worry yourself
on how the goroutine terminates.
2. There are a few annoying issues left if you resolve question 1 above. One of
the problems is that the goroutine isn’t neatly cleaned up when main.main()
exits. And worse, due to a race condition between the exit of main.main()
and main.shower() not all numbers are printed. It should print up until 9, but
sometimes it prints only to 8. Adding a second quit-channel you can remedy
both issues. Do this.
▷ Answer
Fibonacci II
Write a function that takes an int value and gives that many terms of the
Fibonacci sequence.
▷ Answer
Chapter 8.
Communication
https://www.miek.nl/go/ 69/85
12/28/24, 2:08 PM Learning Go
In this chapter we are going to look at the building blocks in Go for communicating
with the outside world. We will look at files, directories, networking and executing
other programs. Central to Go’s I/O are the interfaces io.Reader and io.Writer.
The io.Reader interface specifies one method Read(p []byte) (n int, err
err).
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
}
}
We open the file at 1 with os.Open that returns a *os.File *os.File implements
io.Reader and io.Writer interface. After the Open we directly put the f.Close()
which we defer until the function return. At 3 we call Read on f and read up to
1024 bytes at the time. If anything fails we bail out at 4 . If the number of bytes
read is 0 we’ve read the end of the file 5 . And at 6 we output the buffer to
standard output.
https://www.miek.nl/go/ 70/85
12/28/24, 2:08 PM Learning Go
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.
io.Reader
As mentioned above the io.Reader is an important interface in the language Go.
A lot (if not all) functions that need to read from something take an io.Reader as
input. To fulfill the interface a type needs to implement that one method. The
writing side io.Writer, has the Write method.
https://www.miek.nl/go/ 71/85
12/28/24, 2:08 PM Learning Go
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!
Some examples
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).
A more robust method (but slightly more complicated) is ReadLine, see the
documentation of the bufio package.
A common scenario in shell scripting is that you want to check if a directory exists
and if not, create one.
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).
https://www.miek.nl/go/ 72/85
12/28/24, 2:08 PM Learning Go
has a more sophisticated interface, and also provides a way to parse flags. Take
this example from a DNS query tool:
At 1 we define a bool flag -dnssec. Note that this function returns a pointer to the
value, the dnssec is now a pointer to a bool. At 2 we define an strings flag.
Then at 3 we redefine the Usage variable of the flag package so we can add some
extra text. The PrintDefaults at 4 will output the default help for the flags that
are defined. Note even without redefining a flag.Usage the flag -h is supported
and will just output the help text for each of the flags. Finally at 4 we call Parse
that parses the command line and fills the variables.
After the flags have been parsed you can used them: if *dnssec { ... }
Executing commands
The os/exec package has functions to run external commands, and is the premier
way to execute commands from within a Go program. It works by defining a
*exec.Cmd structure for which it defines a number of methods. Let’s execute ls -
l:
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.
https://www.miek.nl/go/ 73/85
12/28/24, 2:08 PM Learning Go
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 this18:
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. 19
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))
}
}
https://www.miek.nl/go/ 74/85
12/28/24, 2:08 PM Learning Go
Exercises
Finger daemon
[RFC1196]
Fingerd is a simple daemon based on RFC 1196 that provides an
interface to the “finger” program at most network sites. The program is
supposed to return a friendly, human-oriented status report on either the
system at the moment or a particular person in depth.
Stick to the basics and only support a username argument. If the user has a .plan
file show the contents of that file. So your program needs to be able to figure out:
▷ Answer
Echo server
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.
Make the server concurrent so that every request is taken care of in a separate
goroutine.
▷ Answer
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.
https://www.miek.nl/go/ 75/85
12/28/24, 2:08 PM Learning Go
▷ Answer
Uniq
Write a Go program that mimics the function of the Unix uniq command. This
program should work as follows, given a list with the following items:
'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 = $_; }
}
▷ Answer
Quine
▷ Answer
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:
For acquiring the process list, you’ll need to capture the output of ps -e -
opid,ppid,comm. This output looks like:
https://www.miek.nl/go/ 76/85
12/28/24, 2:08 PM Learning Go
If a parent has one child you must print child, if there is more than one print
children.
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{$_}}]";
}
▷ Answer
Number cruncher
An example. We have picked the numbers: 1, 6, 7, 8, 8 and 75. And i is 977. This
can be done in many different ways, one way is:
((((1 ∗ 6) ∗ 8) + 75) ∗ 8) − 7 = 977 or (8 ∗ (75 + (8 ∗ 6))) − (7/1) = 977
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.
https://www.miek.nl/go/ 77/85
12/28/24, 2:08 PM Learning Go
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.
▷ Answer
Bibliography
[RFC1196]
[RFC4627]
[bubblesort]
Wikipedia Bubble sort 2010
[csp]
C. A. R. Hoare Communicating sequential processes (csp) 1985
[duck_typing]
Wikipedia Duck typing 2010
[effective_go]
Go Authors Effective Go 2010
[fizzbuzz]
Imran On Tech Using fizzbuzz to find developers... 2010
[go_1_5_release_notes]
Go Authors Go 1.5 Release Notes 2010
[go_blog_panic]
Go Authors Defer, panic, and recover 2010
[go_course_day3]
Rob Pike The Go programming language, day 3 2010
[go_interfaces]
Ian Lance Taylor Go interfaces 2010
[go_nuts_interfaces]
Go Community Function accepting a slice of interface types 2010
[go_spec]
Go Authors Go language specification 2010
https://www.miek.nl/go/ 78/85
12/28/24, 2:08 PM Learning Go
[go_tutorial]
Go Authors Go tutorial 2010
[go_web]
Go Authors Go website 2010
[iota]
Wikipedia Iota 2010
Footnotes
1. Yes, that sounds a lot like coroutines, but goroutines are slightly different as
we will see in Chapter 8. Communication. [return]
2. http://play.golang.org. [return]
3. http://golang.org/doc/ itself is served by godoc. [return]
4. When building from source it must be installed separately with go get
golang.org/x/tools/cmd/godoc. [return]
5. The word iota is used in a common English phrase, ‘not one iota’, meaning
‘not the slightest difference’, in reference to a phrase in the New Testament:
“until heaven and earth pass away, not an iota, not a dot, will pass from the
[iota]
[return]
Law.”
6. You can use the command godoc builtin to read the online documentation
about the built-in types and functions. [return]
7. Always rainy in March anyway. [return]
8. This is a motto of Go; “Do more with less code”. [return]
9. A function literal is sometimes called a closure . [return]
10. Modified from a presentation of Eleanor McHugh. [return]
11. The descriptions are copied from the packages’ go doc. [return]
12. The downside is that you know have to worry about garbage collection. If
you really need it garbage collection in a Go program can be disabled by
running it with the environment variable GOGC set to off: GOGC=off
./myprogram. [return]
13. Taking the address of a composite literal tells the compiler to allocate it on
the heap, not the stack. [return]
https://www.miek.nl/go/ 79/85
12/28/24, 2:08 PM Learning Go
Index
a
array
multidimensional [go]
b
built-in
close [go]
delete [go]
len [go]
cap [go]
new [go] [go]
make [go] [go]
copy [go] [go]
append [go] [go]
panic [go]
recover [go]
print [go]
complex [go]
real [go]
imag [go]
https://www.miek.nl/go/ 80/85
12/28/24, 2:08 PM Learning Go
c
channel
unbuffered [go]
blocking read [go]
blocking write [go]
non-blocking read [go]
non-blocking write [go]
d
duck
typing [go]
f
field [go]
functions
receiver [go]
method [go]
pass-by-value [go]
as values [go]
literals [go]
signature [go]
literal [go]
variadic [go]
exported [go]
private [go]
public [go]
g
generic [go]
goroutine [go] [go]
i
interface [go]
set of methods [go]
https://www.miek.nl/go/ 81/85
12/28/24, 2:08 PM Learning Go
type [go]
value [go]
io
buffered [go]
io.Reader [go]
k
keywords
iota [go]
if [go]
return [go]
else [go]
goto [go]
for [go]
break [go]
continue [go]
range [go] [go] [go] [go]
switch [go]
fallthrough [go]
default [go]
map [go]
map adding elements [go]
map existence [go]
map remove elements [go]
defer [go]
defer list [go]
package [go]
import [go]
type [go]
struct [go]
go [go]
l
label [go]
literal
composite [go] [go]
https://www.miek.nl/go/ 82/85
12/28/24, 2:08 PM Learning Go
m
methods
inherited [go]
n
networking
Dial [go]
nil [go]
o
operators
bitwise and [go]
bitwise or [go]
bit wise xor [go]
bitwise clear [go]
and [go]
or [go]
not [go]
address-of [go]
increment [go]
channel [go]
p
package [go]
builtin [go]
fmt [go] [go]
bytes [go]
bufio [go] [go] [go]
ring [go]
io [go] [go]
sort [go]
strconv [go]
os [go]
sync [go]
flag [go] [go]
encoding/json [go]
https://www.miek.nl/go/ 83/85
12/28/24, 2:08 PM Learning Go
html/template [go]
net/http [go]
unsafe [go]
reflect [go] [go]
os/exec [go] [go]
r
reference types [go]
runes [go] [go]
s
scope
local [go] [go]
slice
capacity [go]
length [go]
structures
embed [go]
t
tooling
go [go]
go build [go]
go run [go]
go test [go]
v
variables
declaring [go]
assigning [go]
parallel assignment [go]
underscore [go]
https://www.miek.nl/go/ 84/85
12/28/24, 2:08 PM Learning Go
https://www.miek.nl/go/ 85/85