Table of Contents
- Preface
- How to use this eBook
- 1. Introduction
- 2. Learn Go programming
- 3. Variables, Constants, and Types
- 4. Control Structures
- 5. Pointers
- 6. More on Functions
- 7. Detailed information
- 8. Arrays
- 9. Slices
- 10. Range
- 11. Maps
- 12. Structs
- 13. interface
- 14. Error
- 15. io, os and net/http - Getting started
- 16. Random Numbers
- 17. File Handling
- 18. JSON and Go
- 19. TCP programming using Go
- 20. Unit testing and Benchmarking Go programs
- 21. Concurrency
- 22. Additional Exercises
- 23. Additional Reading
- Appendix A
- Appendix B - Project: redditnews for Baby Gophers
- Appendix C - Build, deploy webapps to cloud
Preface
Who is the eBook for?
These are the study notes, in eBook form, I made when I learned Go programming and is an introduction to the Go language. The intended audience are people who are familiar with programming and know some programming language. This does not teach you how to program, rather it teaches you how to use Go.
What will you learn?
In the end, you will understand and know the basics of the Go language.
Using code examples
All of the code in this eBook can be used pretty much anywhere and anyway you please.
Errata
Although I have taken every care to ensure the accuracy of the content of this eBook, mistakes do happen. If you find a mistake in this eBook — maybe a mistake in the text or the code — I would be grateful if you would report this to me. By doing so, you can save other readers from frustration and help me improve subsequent versions of this eBook. If you find any errata, please report them by sending me an email.
Acknowledgements
I have made extensive references to the following:
- How to Write Go Code1
- A Tour of Go2
- Go Language Specification3
- Effective Go4
- Go FAQ5
- 50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs6
I’d like to thank the Go community who have written excellent articles, blog posts on Go. I have referred to many such articles and blog posts, while I learned Go, and given them due credit in this eBook.
There are a good number of people who deserve thanks for their help and support they provided, either while or before this eBook was written, and there are still others whose help will come after the eBook is released. For help with proof reading, checking exercises and text improvements, I would like to thank Anders Persson7, Basant Singh8, Christian Hujer, Gunnar Aasen, Jose Carlos Monteiro9, Kevin Lozandier10, Kiran Mysore11, Kirit Ayya12, Marcelo Magallon13, Nilesh Govindrajan14, Peggy Li15, Prathamesh Sonpatki16, Shawn Milochik, Valindo Godinho17 and Victor Goff18.
The Gopher character is based on the Go mascot designed by Renée French19 and copyrighted under the Creative Commons Attribution 3.0 license.
How to contact me?
I can be reached via e-mail at satish.talim@gmail.com. Please contact me if you have any questions, comments, kudos or criticism on the eBook. Constructive criticism is definitely appreciated; I want this eBook to get better through your feedback.
Thanks
Thanks for downloading and checking out this eBook. As part of the lean publishing philosophy, you’ll be able to interact with me as the eBook is completed. I’ll be able to change things, reorganize parts, and generally make a better eBook. I hope you enjoy.
- https://golang.org/doc/code.html↩
- https://tour.golang.org/welcome/1↩
- https://golang.org/ref/spec↩
- https://golang.org/doc/effective_go.html↩
- http://golang.org/doc/faq#unused_variables_and_imports↩
- http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/↩
- https://twitter.com/uandersp↩
- https://twitter.com/SinghBasant↩
- https://github.com/straider↩
- https://github.com/lozandier↩
- https://twitter.com/techgravity↩
- https://twitter.com/kirit_ayya↩
- https://github.com/mem↩
- https://twitter.com/nileshgr↩
- https://twitter.com/_peggyli↩
- https://twitter.com/_cha1tanya↩
- https://twitter.com/Valindogodinho↩
- https://twitter.com/kotp↩
- http://reneefrench.blogspot.in/↩
How to use this eBook
With the help of this eBook, you should:
- Go through each exercise.
- Type in each sample exactly.
- Make it run.
If you go through this eBook, and do each exercise for one or two hours a night, you will have a good foundation for moving onto another book about Go to continue your studies. This eBook won’t turn you into a Go expert overnight, but it will get you started on the path to learning how to code in Go.
Remember:
- Typing the code samples and getting them to run will help you learn the names of the symbols, get familiar with typing them, and get you reading the language.
- You must pay attention to the tiniest details of your work or you will miss important elements of what you create. By going through this eBook, and copying each example exactly, you will be training your brain to focus on the details of what you are doing, as you are doing it.
- While you do these exercises, typing each one in, you will be making mistakes. It’s inevitable; even seasoned programmers would make a few. Your job is to compare what you have written to what’s required, and fix all the differences. By doing so, you will train yourself to notice mistakes, bugs, and other problems.
- You must type each of these exercises in, manually. If you copy and paste, you might as well not even do them. The point of these exercises is to train your hands, your brain, and your mind in how to read, write, and see code. If you copy-paste, you are cheating yourself out of the effectiveness of the lessons.
I know that to get good at anything you have to practice every day, even if it’s difficult. Keep trying and eventually it’ll be easier and fun.
If you break the problem down into small exercises and lessons, and do them every day, you can learn to do almost anything. If you focus on slowly improving and enjoying the learning process, then you will benefit no matter how good you are at it.
Remember whatever your reason for wanting to quit, don’t. Victor Goff and I are available on the slack channel1 to help you in the learning process.
- https://docs.google.com/forms/d/1WXO68p3gH4b-4S3dOS_MUbvoe7uaRNT9tii1syTznYA/viewform↩
1. Introduction
1.1 What is Go?
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.
Go comes with a great standard library and an enthusiastic community.
1.2 Go Recent History
The history of the language before its first release, back to 2007, is covered in the language’s FAQ2.
The mascot and logo were designed by Renée French3. The logo and mascot are covered by the Creative Commons Attribution 3.0 license.
The current version of Go is 1.5. Development will continue on Go itself, but the focus will be on performance, reliability, portability and the addition of new functionality such as improved support for internationalization.
1.3 Go Authors
Go was initially developed at Google in 2007 by Robert Griesemer4, Rob Pike5 and Ken Thompson6.
Go became a public open source project on November 10, 2009. Many people from the community have contributed ideas, discussions, and code.
1.4 Why Go?
Very briefly:
- Compiled, instead of interpreted or byte-coded for a virtual machine
- Fast, to compile and to execute
- Safe because it’s Statically Typed, instead of dynamically typed
- C-like Syntax, but much cleaner and simpler (less noise, less verbose), with a self-imposed convention for its Coding Style
- Type Inference and Postfix Types
- Garbage Collector
- UTF-8 encoding mode, by default
- Concurrency, with Channels and GoRoutines
- Open Source
2. Learn Go programming
This eBook is an introduction to the Go language. The intended audience are people who are familiar with programming and know some programming language. This eBook does not teach you how to program, rather it teaches you how to use Go.
We shall be using the Go Playground1 to write and run most of the programs in this eBook.
I have purposely left the details of installing Go on your local computer and its various settings in Appendix A, as I first want you to get your hands wet with writing Go code!
There are limitations to the programs that can be run in the playground, which please be aware.
As such, there are certain programs in the eBook that will not run in the Go Playground. I shall tell you which programs won’t run.
Once the Go Playground loads up, clear whatever is written on the screen and type our first program:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
fmt
.
Printf
(
"Hello World"
)
7
}
Click on the Run button and you will see “Hello World” at the bottom of the screen.
Let’s quickly understand the above program:
- Go programs are organized as packages.
- The
import
statement allows you to use external code. Thefmt
package, in this case, which is provided by the standard library allows you to format and print data. - The
main()
function is what gets executed when you run your application - just like in C.
Let’s look at another bigger program in more detail:
1
// Single line comment. Line comments start with the
2
// character sequence // and stop at the end of the
3
// line. A line comment acts like a newline.
4
// Comments do not nest.
5
6
/*
7
Multi-
8
line comment
9
*/
10
11
/*
12
Package main.
13
All Go files start with package <something>.
14
package main is required for a standalone executable.
15
*/
16
package
main
17
18
import
"fmt"
19
20
func
main
()
{
21
// Multiple statements separated by ;
22
fmt
.
Printf
(
"Hello "
);
fmt
.
Printf
(
"World\n"
)
23
24
// Single statement then ; not needed
25
fmt
.
Printf
(
"Learning Go\n"
)
26
}
Once the Go Playground loads up, clear whatever is written on the screen and type out the above program.
Click on the Run button and check the output at the bottom of the screen.
Let’s understand this program:
2.1 Comments
Go provides:
-
/* */
are block comments. Block comments appear mostly as package comments, but are useful within an expression or to disable many lines of code. - Line comments start with the
//
character sequence and stop at the end of the line. - A line comment acts like a newline.
- Comments do not nest.
Line comments are the norm.
2.2 Statements
The Go code is structured in statements. A statement doesn’t need to end with a ;
. The Go compiler automatically inserts semicolons at the end of statements. However if multiple statements are written on one line (a practice which is not encouraged for readability reasons), they must be separated by ;
.
For more information, please read “Semicolons” in a later chapter.
2.3 Identifiers
Identifiers name program entities such as variables and types.
- An identifier is a sequence of one or more letters and digits.
- The first character in an identifier must be a letter.
- Identifiers are case-sensitive.
2.3.1 Blank identifier
The _
itself is a special identifier, called the blank identifier. It can be used in declarations or variable assignments like any other identifier (and any type can be assigned to it), but its value is discarded, so it cannot be used anymore in the code that follows.
2.3.2 Pre-declared identifiers
The following identifiers are implicitly declared:
Types | bool, byte, complex64, complex128, error, float32, |
float64, int, int8, int16, int32, int64, rune, string, | |
uint, uint8, uint16, uint32, uint64, uintptr | |
Constants | true, false, iota |
Null or no value | nil |
Functions | append, cap, close, complex, copy, delete, imag, len, |
make, new, panic, print, println, real, recover |
2.4 Keywords
This is the set of 25 keywords5 or reserved words and may not be used as identifiers.
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 |
2.5 package
- All Go files start with
package <something>
. - In Go,
package
is always first, thenimport
, then everything else. -
package main
is required for a standalone executable.. - Every package should have a package comment, a block comment preceding the package clause.
- The package comment should introduce the package and provide information relevant to the package as a whole.
- Package comments should begin with the name of the thing being described and end in a period.
- By convention, packages are given lower case, single-word names; there should be no need for underscores or mixedCaps.
- Go’s convention is that the package name is the last element of the import path: the package imported as “crypto/rot13” should be named
rot13
.
2.6 import
-
import
declaration declares library packages referenced in this file. - The package names are enclosed within
""
. - Apart from package
main
we are also using thefmt
package. - A Go program is created by linking together a set of packages through the
import
keyword. -
import "fmt"
tells Go that this program needs (functions, or other elements, from) the package fmt6, which implements functionality for formatted IO. -
import
loads the public declarations from the compiled package, it does not insert the source code. - The Go installation contains a number of ready-to-use packages, which form the standard library. The entire standard library is documented, with examples, at http://golang.org/pkg/7.
If you are importing a single package then you write it like:
import "fmt"
If multiple packages are needed, they can each be imported by a separate statement:
1
import
(
2
"fmt"
3
"os"
4
)
Only apply this when there is more than one entry; in that case it is also clearer to list the package names in alphabetical order.
Note: importing a package which is not used in the rest of the code is a build-error (for example: imported and not used: os
).
imports10 are organized in groups, with blank lines between them. The standard library packages are in the first group. For example:
1
package
main
2
3
import
(
4
"fmt"
5
"hash/adler32"
6
"os"
7
8
"appengine/user"
9
"appengine/foo"
10
11
"code.google.com/p/x/y"
12
"github.com/foo/bar"
13
)
The above is a convention.
- After importing a package, you can refer to the names it exports.
- In Go, a name is exported if it begins with a capital letter.
Foo
is an exported name, as isFOO
. The namefoo
is not exported. A concrete example is in our use offmt.Println
, note thePrintln
with “P” not “p”. - In Go, the use of the words public and private is really not accurate. It is more accurate to say an identifier is exported or not exported from a package.
- When an identifier is exported from a package, it means the identifier can be directly accessed from any other package in the code base.
- When an identifier is not exported from a package, it can’t be directly accessed from any other package.
After the import statement, zero or more constants (const), variables (var), and types (type) can be declared; these are global (have package scope) and are known to all functions in the code, and they are followed by one or more functions (func).
2.6.1 Aliasing of imports
Go allows the aliasing of imports.
1
import
tpl
"text/template"
2
txt
,
err
:=
tpl
.
New
(
"test"
).
Parse
(
"Hello {{.Name}}"
)
`
The code tpl "text/template"
imports the text/template
package under the name tpl
.
Then the application can call functions in text/templete
under the alias name, as shown above.
Aliases are a great (and necessary) feature of Go package management. But don’t go overboard. Conventionally, aliases are only used when necessary (such as when package names would otherwise collide). Code is more readable without unnecessary aliases.
2.6.2 Removing explicit package reference
We might choose to simplify matters a little by importing the fmt
namespace into the namespace of the current source file, which requires we change our import
statement.
import . "fmt"
This consequently allows the explicit package reference to be removed from the Println()
function call.
Println("hello world")
1
package
main
2
3
import
.
"fmt"
4
5
func
main
()
{
6
Println
(
"hello world"
)
7
}
1
package
main
2
3
import
"fmt"
4
import
"os"
5
6
func
main
()
7
{
8
fmt
.
println
(
"Hello, world."
)
9
}
Please use this link13 to run the program.
2.7 functions
-
func main() {
is a function definition.main
is special. It is the entry point for the executable program. - The requirements for the compiler to produce an executable are belonging to
package main
and declaring themain()
function. This function receives no inputs and returns no result, it’s more like a “procedure” in some other languages. - Thus
main
has no parameters and no return type. - Go uses brace brackets. The first { must be on the same line as the func-declaration: this is imposed by the compiler. The last } is positioned after the function-code in the column beneath function.
-
fmt.Printf("")
here we call a functionPrintf
from the packagefmt
to print a string to the screen. - In Go, we use a dot notation to access the function
Printf
of the packagefmt
.
For more information on functions, please read “More on Functions” in a later chapter.
2.8 String literal
- Interpreted string literals are UTF-8 characters between double quotes
""
and may contain non-ASCII characters. - Once assigned to a variable the string can not be changed: strings in Go are immutable.
For more information on strings, please read “More on Strings” in a later chapter.
Go source code is Unicode text encoded in UTF-8.
Go programs are made of keywords, constants, variables, operators, types and functions.
2.9 Numbers
Go has several different types to represent numbers. Generally, we split numbers into two different kinds: integers and floating-point numbers.
2.9.1 Integers
Integers – like their mathematical counterpart – are numbers without a decimal component (-3, -2, -1, 0, 1,).
Our system is made up of 10 different digits. Once we’ve exhausted our available digits we represent larger numbers by using 2 (then 3, 4, 5, …) digits put next to each other. For example the number after 9 is 10, the number after 99 is 100 and so on. Computers do the same, but they only have 2 digits instead of 10. So counting looks like this: 0, 1, 10, 11, 100, 101, 110, 111 and so on. The other difference between the number system we use and the one computers use is that all of the integer types have a definite size. They only have room for a certain number of digits. So a 4 bit integer might look like this: 0000, 0001, 0010, 0011, 0100. Eventually we run out of space and most computers just wrap around to the beginning (which can result in some very strange behavior).
Go’s integer types are:
uint8
, uint16
, uint32
, uint64
, int8
, int16
, int32
and int64
.
8, 16, 32 and 64 tell us how many bits each of the types use. uint
means “unsigned integer” while int
means “signed integer”. Unsigned integers only contain positive numbers (or zero). In addition there two alias types: byte
which is the same as uint8
and rune
which is the same as int32
. Bytes are an extremely common unit of measurement used on computers (1 byte = 8 bits, 1024 bytes = 1 kilobyte, 1024 kilobytes = 1 megabyte, …) and therefore Go’s byte
data type is often used in the definition of other types.
There are also 3 machine dependent integer types:
uint
, int
and uintptr
.
They are machine dependent because their size depends on the type of architecture you are using.
Generally, if you are working with integers you should just use the int
type.
2.9.2 Floating Point Numbers
Floating point numbers are numbers that contain a decimal component (real numbers eg. 1.234, 123.4, 0.00001234, 12340000.0). Their actual representation on a computer is fairly complicated and not really necessary to know in order to know how to use them.
We need only to keep the following in mind:
-
Floating point numbers are inexact. Occasionally, it is not possible to accurately calculate a number. For example computing
1.01 - 0.99
results in0.020000000000000018
– A number extremely close to what we would expect, but not exactly the same. We can, of course, represent 0.02 with no difficulty. - Like integers, floating point numbers have a certain size (32 bit or 64 bit). Using a larger sized floating point number increases it’s precision (how many digits it can represent).
- There are no constants denoting the IEEE-754 infinity and not-a-number values, but the math14 package’s Inf15, NaN16, IsInf17, and IsNaN18 functions return and test for those values at run time.
Go has two floating point types: float32
and float64
(also often referred to as single precision and double precision respectively) as well as two additional types for representing complex numbers (numbers with imaginary parts): complex64
and complex128
.
Floating point precision is finite and you should always be very cautious when making comparisons on real numbers.
Generally, we should stick with float64
when working with floating point numbers.
Reference: The above write-up on Integers and Floating Point Numbers has been referred from the excellent eBook “An Introduction to Programming in Go19” by Caleb Doxsey.
Later on do read up on the big package.
2.9.3 Some default values
Type | Initialized Value |
---|---|
Boolean | false |
Integer | 0 |
Floating Point | 0.0 |
String |
"" (empty string) |
Note: When memory is allocated to store a value, either through a declaration or a call of make
or new
, and no explicit initialization is provided, the memory is given a default initialization. Each element of such a value is set to the zero value for its type: false for booleans, 0 for integers, 0.0 for floats, “” for strings, and nil for pointers, functions, interfaces, slices, channels, and maps.
2.10 Console output
2.10.1 Print and Println
The function Print
print formats using the default formats for its operands and writes to standard output. Spaces are added between operands when neither is a string.
The function Println
formats using the default formats for its operands and writes to standard output. Spaces are always added between operands and a newline is appended.
If you have Go installed on your local machine, then you can read the documentation of any of these functions. Example:
godoc fmt Println
2.10.2 Formatting using Printf
Function Printf
from the package fmt
use the following format verbs:
General | %v | the value in a default format |
Boolean | %t | the word true or false |
Integer | %d | base 10 |
Floating-point and complex | %f | decimal point but no exponent, e.g. 123.456 |
%e | scientific notation, e.g. -1234.456e+78 | |
%g | %e for large exponents, %f otherwise | |
String | %s | string |
The default value for %v is:
bool | %t |
int, int8 etc. | %d |
uint, uint8 etc. | %d |
float32, complex64, etc. | %g |
string | %s |
Please refer to fmt.Printf format reference - cheat sheet20
Let’s look at an example.
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
// Formatting booleans
7
fmt
.
Printf
(
"%v\n"
,
false
)
8
fmt
.
Printf
(
"%t\n"
,
true
)
9
10
// Formatting integers
11
fmt
.
Printf
(
"%v\n"
,
987
)
12
fmt
.
Printf
(
"%d\n"
,
345
)
13
14
// Formatting floats
15
fmt
.
Printf
(
"%v\n"
,
987123456.987345123432
)
16
fmt
.
Printf
(
"%f\n"
,
987123456.987345123432
)
17
// Use %g for large exponents
18
fmt
.
Printf
(
"%g\n"
,
987123456.987345123432
)
19
20
// When formatting numbers you will often
21
// want to control the width and precision
22
// of the resulting figure. To specify the
23
// width of an integer, use a number after
24
// the % in the verb. By default the result
25
// will be right-justified and padded with
26
// spaces.
27
fmt
.
Printf
(
"|%6d|%6d|\n"
,
23
,
987
)
28
29
// You can also specify the width of printed
30
// floats, though usually you'll also want to
31
// restrict the decimal precision at the same
32
// time with the width.precision syntax.
33
fmt
.
Printf
(
"|%6.2f|%6.2f|\n"
,
3.2
,
4.37
)
34
35
// To left-justify, use the - flag.
36
fmt
.
Printf
(
"|%-6.2f|%-6.2f|\n"
,
3.2
,
4.37
)
37
38
// So far we’ve seen Printf, which prints
39
// the formatted string to os.Stdout.
40
// Sprintf formats and returns a string
41
// without printing it anywhere.
42
s
:=
fmt
.
Sprintf
(
"a %s"
,
"string"
)
43
fmt
.
Println
(
s
)
44
45
fmt
.
Println
(
2001
,
3456
)
46
fmt
.
Print
(
20
,
30
)
47
}
Once the Go Playground loads up, clear whatever is written on the screen and type out the above program.
Click on the Run button and check the output at the bottom of the screen.
false
true
987
345
9.871234569873451e+08
987123456.987345
9.871234569873451e+08
|
23
|
987
|
|
3.20
|
4.37
|
|
3.20
|
4.37
|
a
string
2001
3456
20
30
2.11 Console input
The functions Scan
, Scanf
and Scanln
from the fmt
package read from os.Stdin
.
Scan
scans text read from standard input, storing successive space-separated values into successive arguments. Newlines count as space. It returns the number of items successfully scanned.
Scanf
scans text read from standard input, storing successive space-separated values into successive arguments as determined by the format. It returns the number of items successfully scanned. Scanf
parses the arguments according to a format string, analogous to that of Printf
with certain restrictions.
Scanln
is similar to Scan
, but stops scanning at a newline and after the final item there must be a newline or EOF.
Write the following program and store it in the same folder where you had stored hello.go
:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
var
username
string
7
8
fmt
.
Println
(
"Enter Username: "
)
9
fmt
.
Scanf
(
"%s"
,
&
username
)
10
11
fmt
.
Println
(
"Username:"
,
username
)
12
}
For Windows users, use fmt.Scanf("%s\n", &username)
instead of fmt.Scanf("%s", &username)
.
Run the program.
We shall learn about var and the & operator in later chapters.
2.12 Command-line arguments - Args
When we invoke a command on the terminal it’s possible to pass the command arguments.
In the command go run hello.go
, run and hello.go are arguments to the go command.
In the package os
we have a variable Args
: var Args []string
Args
hold the command-line arguments, starting with the program name. os.Args
provides access to raw command-line arguments. Note that the first value in this slice is the path to the program, and os.Args[1:]
holds the arguments to the program.
Note: We talk about slices in a later chapter.
Write the program cmd_line_args.go
as follows and store it in the same folder where you had stored hello.go
:
1
package
main
2
3
import
"fmt"
4
import
"os"
5
6
func
main
()
{
7
//You can get individual args with normal indexing
8
fmt
.
Println
(
os
.
Args
[
1
],
os
.
Args
[
2
],
os
.
Args
[
3
],
os
.
Args
[
4
])
9
}
Run this program, as follows:
go
run
cmd_line_args
.
go
a1
a2
a3
a4
The output you will see is something like this:
a1
a2
a3
a4
2.13 Solutions
- https://play.golang.org/↩
- http://play.golang.org/p/sOqjtiXkRF↩
- https://play.golang.org/↩
- http://golang.org/ref/spec#Bootstrapping↩
- http://golang.org/ref/spec#Keywords↩
- http://golang.org/pkg/fmt/↩
- http://golang.org/pkg/↩
- https://godoc.org/golang.org/x/tools/cmd/goimports↩
- http://golang.org/doc/faq#unused_variables_and_imports↩
- https://github.com/golang/go/wiki/CodeReviewComments#imports↩
- https://golang.org/doc/go1compat↩
- https://golang.org/doc/go1compat↩
- http://play.golang.org/p/9vq29NXfno↩
- https://golang.org/pkg/math/↩
- https://golang.org/pkg/math/#Inf↩
- https://golang.org/pkg/math/#NaN↩
- https://golang.org/pkg/math/#IsInf↩
- https://golang.org/pkg/math/#IsNaN↩
- https://www.golang-book.com/books/intro/3↩
- http://golang-examples.tumblr.com/post/86795367134/fmt-printf-format-reference-cheat-sheet↩
- http://play.golang.org/p/7Ofcns7kWR↩
3. Variables, Constants, and Types
Once the Go Playground loads up, clear whatever is written on the screen and type:
1
package
main
2
3
import
"fmt"
4
5
var
x
int
6
7
func
main
()
{
8
p
:=
5
9
var
q
int
10
fmt
.
Println
(
"p is: "
,
p
)
11
fmt
.
Println
(
"q is: "
,
q
)
12
fmt
.
Println
(
"x is: "
,
x
)
13
}
When we run the program, the output is:
p
is
:
5
q
is
:
0
x
is
:
0
3.1 Variable
A variable is a storage location for holding a value. The set of permissible values is determined by the variable’s type.
The var
statement declares a list of variables.
A var
statement can be at package or function level as seen in the above program.
Go can infer the type of the declared type. For integer literals Go infers type int
, for floating-point literals Go infers type float64
, and for complex literals Go infers type complex128
(the numbers in their names refer to how many bits they occupy). The normal practice is to leave types unspecified unless we want to use a specific type that Go won’t infer.
In the above program we have p := 5
which means that if an initializer is present, the type can be omitted; the variable will take the type of the initializer. Inside a function, the :=
short assignment statement can be used in place of a var
declaration with implicit type. Outside a function the :=
construct is not available.
Here Go automatically allocates some memory for the variable p
- the size of the memory allocated here is that required by a single integer. Since we have also said p := 5
, the integer value 5 is assigned to that memory space after the space is allocated. For variable q, no assignment has been stated. However, Go assigns a “zero-value” by default to most data types. For numeric fields, the value is 0.
Also note that p := 5
is the same as var p int = 5
. Observe that in Go we declare the type1 after the variable name as in var q int
. int
is either 32 or 64 bits.
Multiple var
declarations may also be grouped; const
and import
also allow this. Note the use of parentheses:
1
var
(
2
x
int
3
b
bool
4
)
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
:=
35
,
9
Which makes a and b both integer variables and assigns 35 to a and 9 to b.
Declared, but otherwise, unused variables are a compiler error in Go. The following code:
1
package
main
2
3
func
main
()
{
4
var
p
int
5
}
generates this error:
p
declared
and
not
used
Note: A list of common mistakes you can avoid is available3.
Go is statically typed. Every variable has a static type, that is, exactly one type known and fixed at compile time: int
, float32
, *MyType
, []byte
, and so on. If we declare:
1
type
MyInt
int
2
3
var
i
int
4
var
j
MyInt
then i has type int
and j has type MyInt
. The variables i and j have distinct static types and, although they have the same underlying type, they cannot be assigned to one another without a conversion.
Reference: The above has been mentioned in the Go Blog: The Laws of Reflection4
3.2 Constants
Constants are declared like variables, but with the const
keyword. Constants can be character
, string
, boolean
, or numeric values. Constants cannot be declared using the :=
syntax. Numeric constants are high-precision values.
The convention in Go for constants is to use MixedCaps or mixedCaps rather than underscores or all CAPS, to write multiword constant names.
1
const
secretKey
=
23232390
// Good
2
const
SecretKey
=
23232390
// Good
3
const
secret_key
=
23232390
// Bad
4
const
SECRETKEY
=
23232390
// Bad
3.2.1 Typed and Untyped constants
1
// Untyped Constants.
2
const
ui
=
12345
// kind: integer
3
const
uf
=
3.141592
// kind: floating-point
4
const
hi
=
"Hello"
// kind: string
5
6
// Typed Constants
7
const
ti
int
=
12345
// kind: integer with int64 precision restrictions.
8
const
tf
float64
=
3.141592
// kind: floating-point with float64 precision restrictions.
In the code snippet above, hi is an untyped string constant. An untyped constant is just a value, one not yet given a defined type that would force it to obey the strict rules that prevent combining differently typed values.
Rob Pike’s article5 has a detailed coverage of untyped and typed constants.
3.3 Named types
Named types are another way to create user defined types. They provide some interesting feature/functionality that is not always available in other languages. The standard library in Go used named types very effectively.
Two examples:
Clear the Go Playground screen of whatever is written there and type:
1
// Sample program to show how to declare and use a named type.
2
package
main
3
4
import
"fmt"
5
6
// Duration is a named type that represents a duration
7
// of time in Nanosecond.
8
type
Duration
int64
9
10
func
main
()
{
11
// Declare a variable of type Duration
12
var
duration
Duration
13
fmt
.
Println
(
duration
)
14
15
// Declare a variable of type int64 and assign a value.
16
nanosecond
:=
int64
(
10
)
17
18
// Attemped to assign a variable of type
19
// int64 (base type of Duration) to
20
// a variable of type Duration.
21
duration
=
nanosecond
22
23
// error: cannot use nanosecond (type int64) as type
24
// Duration in assignment
25
}
When we run the program, the output is:
main
.
go
:
21
:
cannot
use
nanosecond
(
type
int64
)
as
type
Duration
in
assignment
Clear the Go Playground screen of whatever is written there and type:
1
// Sample program to show how to declare and use a named type.
2
package
main
3
4
import
"fmt"
5
6
// Duration is a named type that represents a duration
7
// of time in Nanosecond.
8
type
Duration
int64
9
10
func
main
()
{
11
// Declare a variable of type Duration
12
var
duration
Duration
13
fmt
.
Println
(
duration
)
14
15
// Declare a variable of type int64 and assign a value.
16
nanosecond
:=
int64
(
10
)
17
18
// Attemped to assign a variable of type
19
// int64 (base type of Duration) to
20
// a variable of type Duration.
21
// duration = nanosecond
22
23
// error: cannot use nanosecond (type int64) as type
24
// Duration in assignment
25
26
// Convert a value of type int64 to type Duration.
27
duration
=
Duration
(
nanosecond
)
28
fmt
.
Println
(
duration
)
29
}
When we run the program, the output is:
0
10
3.4 Boolean types
A boolean type represents the set of boolean truth values denoted by the predeclared constants true
and false
. The boolean type is bool
.
3.5 Operators
Go supports the normal set of numerical operators. The table below lists the current ones and their relative precedence. They all associate from left to right.
Precedence | Operator(s) |
---|---|
Highest | * / % << >> & &^ |
+ - | ^ |
|
== != < <= > >= | |
<- | |
&& | |
Lowest | || |
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 used for integers, floats, complex numbers and strings (adding strings is concatenating them).
3.5.1 Boolean Operators
Syntax | Description/Result |
---|---|
!b | Logical not operator; false if Boolean expression b is true |
a || b |
Short-circuit logical OR operator; true if either Boolean |
expression a or b is true | |
a && b | Short-circuit logical AND operator; true if both Boolean |
expressions a and b are true | |
x < y | true if expression x is less than expression y |
x <= y | true if expression x is less than or equal to expression y |
x == y | true if expression x is equal to expression y |
x != y | true if expression x is not equal toexpression y |
x >= y | true if expression x is greater than or equal to expression y |
x > y | true if expression x is greater than expression y |
3.5.2 Arithmetic Operators
Syntax | Description/Result |
---|---|
+x | x |
-x | The negation of x |
x++ | Increments x by the untyped constant 1 |
x- - |
Decrements x by the untyped constant 1 |
x += y | Increments x by y |
x -= y | Decrements x by y |
x *= y | Sets x to x multiplied by y |
x /= y | Sets x to x divieded by y; if the numbers are integers any |
remainder is discarded; division by zero causes a runtime panic | |
x + y | The sum of x and y |
x - y | The result of subtracting y and x |
x * y | The result of multiplying y and x |
x / y | The result of dividing y and x; if the numbers are integers any |
remainder is discarded; division by zero causes a runtime panic |
Although Go has sensible rules of precedence, it is recommended to use parentheses to make intentions clear. Using parentheses is particularly recommended for programmers who use multiple languages so as to avoid subtle mistakes.
3.5.3 Bitwise Operators
There are many good online tutorials on Bitwise operators and we shall not be talking about the same. Here’s one6.
Just remember that many languages use ~
as the unary NOT
operator (i.e. bitwise complement), but Go reuses the XOR
operator (^
) for that. Go also has a special ‘AND NOT’ bitwise operator (&^
).
3.6 Solutions
- http://golang.org/ref/spec#Types↩
- https://github.com/golang/go/wiki/CodeReviewComments#variable-names↩
- https://github.com/golang/go/wiki/CodeReviewComments↩
- http://blog.golang.org/laws-of-reflection↩
- https://blog.golang.org/constants↩
- http://code.tutsplus.com/articles/understanding-bitwise-operators–active-11301↩
- http://play.golang.org/p/LtFP2Gkpdi↩
- http://play.golang.org/p/-3EAKb4ZGe↩
- http://play.golang.org/p/NZ1N721NbY↩
- http://play.golang.org/p/pol2hdWsTy↩
4. Control Structures
4.1 if-else
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
x
,
y
:=
30
,
45
7
if
x
<
y
{
8
fmt
.
Println
(
"x is less than y"
)
9
}
else
if
x
>
y
{
10
fmt
.
Println
(
"x is greater than y"
)
11
}
else
{
12
fmt
.
Println
(
"x is equal to y"
)
13
}
14
}
Run the above program and the output that you will see is:
x
is
less
than
y
Note:
- There may be zero or more
else if
clauses and zero or one finalelse
clause. Each block consists of zero or more statements. - The expression after the
if
and theelse if
has to evaluate to one of thebool
types:true
orfalse
. - There are no parentheses required around the boolean expression - you can have a parentheses, but you don’t have to have it.
-
Go requires the curly braces and the
if else
keywords to be on the same line with the corresponding braces. If not you will see errors. - You can have the explicit values like
true
orfalse
, or you can also use any expression that evaluates to atrue
orfalse
. - Note that types like an
int
cannot be used as truth values - this is unlike languages like C, where you can use integers and pointers as truth values.
4.2 goto statement
Go has a goto
statement, which you should use wisely. With goto
you jump to a label which must be defined within the current function. For instance:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
i
:=
0
7
Label
:
// First word on a line ending with a colon is a label
8
fmt
.
Println
(
i
)
9
i
++
10
if
i
>
50
{
11
goto
Escape
12
}
13
14
goto
Label
// Jump
15
Escape
:
// Label
16
fmt
.
Println
(
"Get out of here..."
)
17
}
The name of the label is case sensitive.
4.3 switch
The general format of the switch is:
1
switch
any
expression
of
type
T
{
2
case
any
expression
of
same
type
T
:
{
code
to
be
executed
if
the
case
expression
is
equal
\
3
to
the
switch
expression
}
4
case
any
expression
of
same
type
T
:
{
code
to
be
executed
if
this
case
expression
is
equal
\
5
to
the
switch
expression
}
6
//any number of case statements are allowed
7
default
:
{
code
to
be
executed
if
none
of
the
case
expressions
match
the
switch
expression
\
8
}
9
}
Points to note:
- the type of the evaluated value in the
switch
and thecase
should match. For example, if theswitch
expression evaluates to anint
and the case expression evaluates to astring
, then there will be a compile error - if there is no expression at all in the
switch
, then by default it isbool
. The block that evaluates totrue
is executed, and none of the others are executed - if more than one
case
statements match, then the first in the lexical order is executed - unlike certain other languages, each
case
block is independent and the code does not “fall through” (each of thecase
code blocks are like independentif-else
code blocks.) - the
default
code block is executed if none of the othercase
blocks match - the
case
expressions need not be just constants and can have expressions - the use of curly braces for demarcating the code blocks is optional
- the
default
statement is optional - any number of
case
statements are allowed - even zerocase
statements are valid, though that wouldn’t be very useful - multiple expressions can be used, each separated by commas in the
case
statement - the
default
block can be anywhere within theswitch
block, and not necessarily last in the lexical order
Please refer to Control structures - Go switch case statement1 for more information.
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
switch
{
7
default
:
8
fmt
.
Println
(
"I am default"
)
9
case
false
:
10
fmt
.
Println
(
"I am false"
)
11
case
true
:
12
fmt
.
Println
(
"I am true"
)
13
}
14
}
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
i
:=
5
7
switch
i
{
8
case
4
:
9
fmt
.
Println
(
"number 4"
)
10
case
i
>
8
:
11
fmt
.
Println
(
"i is > 8"
)
12
default
:
13
fmt
.
Println
(
"default"
)
14
}
15
}
4.3.1 fallthrough statement
In certain languages like C and Java, the switch-case
statement behaves slightly differently from Go: when a case
block is executed, all the case
blocks below it are also executed, unless explicitly terminated (by say using a break
statement). In Go, this is not the default behavior, but if you want to achieve the same result, then use the fallthrough
statement to indicate that the case
block following the current one has to be executed.
1
k
:=
3
2
switch
k
{
3
case
3
:
fmt
.
Println
(
"was <= 3"
);
fallthrough
;
4
case
4
:
fmt
.
Println
(
"was <= 4"
);
fallthrough
;
5
...
4.4 for loop
Go has only one looping construct, the for
loop.
The basic for
loop looks as it does in C or Java, except that the ( )
are gone (they are not even optional) and the { }
are required.
There are three forms of for
, only one of which has semicolons.
1
// Like a C for
2
for
init
;
condition
;
post
{
}
3
4
// Like a C while
5
for
condition
{
}
6
7
// Like a C for(;;)
8
for
{
9
}
Here’s an example:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
sum
:=
0
7
for
i
:=
0
;
i
<
100
;
i
++
{
8
sum
+=
i
9
}
10
fmt
.
Println
(
sum
)
11
}
The output of the program is 4950
.
The above program is available here2.
Here are some code snippets and their explanation:
1
func
main
()
{
2
sum
:=
1
3
// As in C or Java, you can leave the
4
// pre and post statements empty.
5
for
;
sum
<
100
;
{
6
sum
+=
sum
7
}
8
fmt
.
Println
(
sum
)
9
}
1
func
main
()
{
2
sum
:=
1
3
// You can drop the semicolons
4
// for behaves like C's while
5
for
sum
<
1000
{
6
sum
+=
sum
7
}
8
fmt
.
Println
(
sum
)
9
}
1
func
main
()
{
2
// infinite loop
3
for
{
4
}
5
}
The break
keyword allows you to terminate a loop at a point and continue execution at the statement following the end of the for
loop block.
An example:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
i
:=
0
7
for
{
8
if
i
>=
10
{
9
break
10
}
11
fmt
.
Println
(
"Value of i is:"
,
i
)
12
i
++
13
}
14
fmt
.
Println
(
"A statement just after the for loop."
)
15
}
With the continue
keyword you begin the next iteration of the loop, skipping any remaining code. In the same way as break
, continue
also accepts a label.
The following loop prints 0 to 5.
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
for
i
:=
0
;
i
<
10
;
i
++
{
7
if
i
>
5
{
8
// Skip the rest of the remaining code in the loop
9
continue
10
}
11
fmt
.
Println
(
i
)
12
}
13
}
A nested for loop example:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
L
:
7
for
{
8
for
{
9
break
L
10
}
11
}
12
fmt
.
Println
(
"Break out of nested loops"
)
13
}
O
OO
OOO
OOOO
OOOOO
OOOOOO
OOOOOOO
OOOOOOOO
OOOOOOOOO
OOOOOOOOOO
4.5 Solutions
Exercise 6 Reason: If there is no expression at all in the switch
, then by default it is bool
. The block that evaluates to true
is executed, and none of the others are executed.
Exercise 7 Reason: The program will not compile as the type int
and bool
don’t match between the case
and the switch
.
Exercise 9 Solutions. Part a4 and Part b5.
5. Pointers
According to Wikipedia1: A pointer references a location in memory, and obtaining the value stored at that location is known as de-referencing the pointer. As an analogy, a page number in a book’s index could be considered a pointer to the corresponding page; de-referencing such a pointer would be done by flipping to the page with the given page number.
When there is a value stored in memory, it is stored in a physical location. This location is called its address. Many programming languages, including Go, allows you to access the data in a location by specifying its location in memory.
Unlike C, Go has no pointer arithmetic. Do read “Why is there no pointer arithmetic?”2
You obtain the address of a variable by using the &
symbol before the variable name. Let’s look at a simple example:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
i
:=
10
7
fmt
.
Println
(
"The value of i is : "
,
i
)
8
fmt
.
Println
(
"The address of i is: "
,
&
i
)
9
}
The output is:
The
value
of
i
is
:
10
The
address
of
i
is
:
0x1043617c
The actual value of the address will differ from machine to machine and even on different executions of the same program as each machine could have a different memory layout and also the location where it is allocated could be different.
Normal programs do not use the numeric value of the address for anything. Instead, what they usually use is the value referenced by the address. You can get the value at an address by using the *
symbol before the address.
Let’s look at another example:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
v
:=
50
7
8
fmt
.
Println
(
"The value of v is : "
,
v
)
9
fmt
.
Println
(
"The address of v is : "
,
&
v
)
10
fmt
.
Println
(
"The value at address "
,
&
v
,
" is : "
,
*
(
&
v
))
11
}
The output is:
The
value
of
v
is
:
50
The
address
of
v
is
:
0x113a21e0
The
value
at
address
0x113a21e0
is
:
50
When a variable holds an address, it is called a pointer. Therefore in the example:
ptr := &v
ptr
is a pointer and it holds the address the of v. Putting it differently, ptr
is a pointer to the variable v. You could also say that ptr
is a reference to the variable v.
Let’s look at an example:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
i
,
j
:=
20
,
5000
7
8
p
:=
&
i
// point to i
9
fmt
.
Println
(
*
p
)
// read i through the pointer
10
*
p
=
25
// set i through the pointer
11
fmt
.
Println
(
i
)
// see the new value of i
12
13
p
=
&
j
// point to j
14
*
p
=
*
p
/
10
// divide j through the pointer
15
fmt
.
Println
(
j
)
// see the new value of j
16
}
The output is:
20
25
500
Few more examples:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
a
:=
5
7
var
ptr
*
int
8
9
ptr
=
&
a
10
11
fmt
.
Println
(
ptr
)
12
}
The output on my machine is:
0x1043617c
Recollect that the built-in function new
allocates memory. new
can be applied to types like int
, string
etc.
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
// What new will return back to you is a pointer
7
// to a location in memory set aside that can
8
// hold the type you specified.
9
x
:=
new
(
string
)
// Here string is a type
10
11
// A benefit of using new is you can simply
12
// use the variable (pointer) without having to
13
// use the & operator
14
*
x
=
"Hello"
15
fmt
.
Println
(
*
x
)
16
}
The output is:
Hello
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
v
:=
new
(
int
)
7
*
v
++
8
fmt
.
Println
(
*
v
)
9
}
The output is:
1
1
package
main
2
3
func
main
()
{
4
const
i
=
5
5
ptr1
:=
&
i
6
ptr2
:=
&
10
7
}
1
package
main
2
3
func
main
()
{
4
var
p
*
int
// Declare a nil value pointer
5
*
p
=
10
6
}
5.1 Solutions
Exercise 11 Reason: You can only take the pointer to a variable and not to a literal value or a constant.
Exercise 12 Reason: If our code attempts to read or write to address 0x0, the hardware will throw an exception that will be caught by the Go runtime and reported back up to our program in the form of a panic. If the panic is not recovered, a stack trace is produced.
6. More on Functions
Functions are reusable units of code. Considering the black-box analogy, a function is a black unit of code to the outside, which is stimulated by some well known inputs (arguments or parameters) to do some work and then produce an outcome (either an output or a result). Functions are declared / defined once and used many times.
func (p mytype) funcname(q int) (r,s int) { return 0,0 }
- The keyword
func
is used to declare a function. - A function can optionally be bound to a specific type. This is called the receiver.
-
funcname
is the name of your function. - The variable q of type
int
is the input parameter. The parameters are passed pass-by-value meaning they are copied. - Go requires explicit returns, i.e. it won’t automatically return the value of the last expression.
- The variables r and s are the named return parameters for this function. Functions in Go can have multiple return values. 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. -
{ return 0,0 }
is the function’s body. Note thatreturn
is a statement so the braces around the parameter(s) are optional. - Functions can be declared, in a program, in any order you wish. The compiler scans the entire file before execution, so function prototyping is a thing of the past in Go.
- Function names starting with a capital letter are exported outside the package, that is, they are visible and can be used by other packages; then they follow PascalCasing eg. MyFunctionInGo().
- When a function is not exported outside a package; then they follow camelCasing: every new word in the name starts with a capital letter eg. myFunctionInGo().
- Functions are values. Functions can be passed around just like any other value. A function’s type signature describes the types of its arguments and return values.
Examples:
Here are two examples. The first is a function without a return value, while the next one is a simple function that returns its input.
func
subroutine
(
in
int
)
{
return
}
func
identity
(
in
int
)
int
{
return
in
}
6.1 A function can have multiple return values. Some examples:
1
x
,
err
:=
f
()
Here is another example:
1
func
f
()
(
int
,
int
)
{
2
return
5
,
6
3
}
4
5
func
main
()
{
6
x
,
y
:=
f
()
7
}
Note: Three changes are necessary: change the return type to contain multiple types separated by comma, change the expression after the return so that it contains multiple expressions separated by comma and finally change the assignment statement so that multiple values are on the left side of the :=
or =
.
It is legitimate to discard some or all of a function’s return values by assigning them to the blank identifier (_
). However, if no return values are wanted it is more conventional to simply ignore them.
Here’s an example:
1
package
main
2
3
import
"fmt"
4
5
func
multipleVals
()(
string
,
int
){
6
return
"Go-Lang"
,
100
7
}
8
9
func
main
()
{
10
lang
,
number
:=
multipleVals
()
11
fmt
.
Println
(
lang
)
12
fmt
.
Println
(
number
)
13
14
// Ignore the first return value with Blank identifier
15
_
,
anotherNum
:=
multipleVals
()
16
fmt
.
Println
(
anotherNum
)
17
}
The output is:
Go
-
Lang
100
100
6.2 A function can take zero or more arguments
1
func
add
(
x
int
,
y
int
)
int
{
2
return
x
+
y
3
}
When two or more consecutive named function parameters share a type, you can omit the type from all but the last.
1
package
main
2
3
import
"fmt"
4
5
func
add
(
i
,
j
int
)
int
{
6
return
i
+
j
7
}
8
9
func
main
()
{
10
fmt
.
Println
(
add
(
42
,
13
))
11
}
In the above example, we shortened i int, j int
to i, j int
.
This section is referred from A Tour of Go.1
6.3 Named return values
Go’s return values may be named and act just like variables. These names should be used to document the meaning of the return values.
A return statement without arguments returns the current values of the results. This is known as a “naked” return. Naked return statements should be used only in short functions, as with the example shown here. They can harm readability in longer functions.
1
package
main
2
3
import
"fmt"
4
5
func
split
(
sum
int
)
(
x
,
y
int
)
{
6
x
=
sum
*
4
/
9
7
y
=
sum
-
x
8
return
9
}
10
11
func
main
()
{
12
fmt
.
Println
(
split
(
17
))
13
}
This section is referred from A Tour of Go.2
6.4 What’s the idiomatic way to exit a program with some error code?
func Exit(code int)
Exit
causes the current program to exit with the given status code. Conventionally, code zero indicates success, non-zero an error. The program terminates immediately; deferred functions are not run.
You can do something along these lines in most of your real main
packages, so that the return err
convention is adopted as soon as possible, and has a proper termination:
1
// part code
2
func
main
()
{
3
if
err
:=
run
();
err
!=
nil
{
4
fmt
.
Fprintf
(
os
.
Stderr
,
"error: %v\n"
,
err
)
5
os
.
Exit
(
1
)
6
}
7
}
8
9
func
run
()
error
{
10
err
:=
something
()
11
if
err
!=
nil
{
12
return
err
13
}
14
// etc
15
}
1
package
main
2
3
import
"fmt"
4
5
var
i
=
10
6
7
func
main
()
{
8
a
()
9
b
()
10
a
()
11
}
12
13
func
a
()
{
14
fmt
.
Println
(
i
)
15
}
16
17
func
b
()
{
18
i
:=
20
19
fmt
.
Println
(
i
)
20
}
The output when you run the above program is:
10
20
10
1
package
main
2
3
import
"fmt"
4
5
var
i
int
6
7
func
main
()
{
8
i
:=
20
9
fmt
.
Println
(
i
)
10
b
()
11
}
12
13
func
b
()
{
14
i
:=
40
15
fmt
.
Println
(
i
)
16
a
()
17
}
18
19
func
a
()
{
20
fmt
.
Println
(
i
)
21
}
The output when you run the above program is:
20
40
0
6.5 Variadic Functions
We have discussed this along with range in “Variadic Functions using range” in a later chapter.
6.6 Closures
The material on Closures and Recursion has been adapted from An Introduction to Programming in Go.3
It is possible to create functions inside of functions:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
x
:=
0
7
increment
:=
func
()
int
{
8
x
++
9
return
x
10
}
11
fmt
.
Println
(
increment
())
12
fmt
.
Println
(
increment
())
13
}
The output is:
1
2
increment
is a local variable that has the type func() int
(a function that takes no parameters and returns an int
).
increment
adds 1 to the variable x
which is defined in the main
function’s scope. This x
variable can be accessed and modified by the increment
function. This is why the first time we call increment
we see 1 displayed, but the second time we call it we see 2 displayed.
A function like this together with the non-local variables it references is known as a closure. In this case increment
and the variable x
form the closure.
One way to use closure is by writing a function which returns another function which – when called – can generate a sequence of numbers. For example here’s how we might generate odd numbers:
1
package
main
2
3
import
"fmt"
4
5
//This function makeOddGenerator returns
6
//another function, which we define anonymously
7
//in the body of makeOddGenerator
8
func
makeOddGenerator
()
func
()
int
{
9
i
:=
1
10
return
func
()
int
{
11
i
+=
2
12
return
i
13
}
14
}
15
16
func
main
()
{
17
nextOdd
:=
makeOddGenerator
()
18
fmt
.
Println
(
nextOdd
())
19
fmt
.
Println
(
nextOdd
())
20
fmt
.
Println
(
nextOdd
())
21
22
//To confirm that the state is unique
23
//to that particular function, create
24
//and test a new one
25
newOdd
:=
makeOddGenerator
()
26
fmt
.
Println
(
newOdd
())
27
fmt
.
Println
(
newOdd
())
28
fmt
.
Println
(
newOdd
())
29
}
The output is:
3
5
7
3
5
7
Closures are syntactically lighter than a named function. So, if there’s a scenario where a function is used only once or a limited number of times it’s better to go for Closures.
6.7 Recursion
Go supports recursive functions i.e. a function is able to call itself. Here’s a classic factorial example:
1
package
main
2
3
import
"fmt"
4
5
func
fact
(
n
int
)
int
{
6
if
n
==
0
{
7
return
1
8
}
9
return
n
*
fact
(
n
-
1
)
10
}
11
func
main
()
{
12
fmt
.
Println
(
fact
(
8
))
13
}
Closure and recursion are powerful programming techniques which form the basis of a paradigm known as functional programming. Most people will find functional programming more difficult to understand than an approach based on for loops, if statements, variables and simple functions.
6.8 Solutions
Exercise 13 Reason: 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.
Exercise 14 Reason: A local variable is only valid when we are executing the function in which it is defined.
YES…BUT THERE’S MORE…
7. Detailed information
7.1 Semicolons
Like C, Go’s formal grammar uses semicolons to terminate statements1, but unlike in C, those semicolons do not appear in the source.
Idiomatic Go programs have semicolons only in places such as for loop clauses, to separate the initializer, condition, and continuation elements. They are also necessary to separate multiple statements on a line, should you write code that way.
One consequence of the semicolon insertion rules is that you cannot put the opening brace of a control structure (if, for, switch, or select) on the next line. If you do, a semicolon will be inserted before the brace, which could cause unwanted effects. Write them like this:
if
i
<
f
()
{
g
()
}
not like this:
7.2 init
Every Go package may contain one or more init()
functions specifying actions that should be taken during program initialisation. Here, multiple declarations of the same identifier can occur without either resulting in a compilation error or the shadowing of a variable.
1
package
main
2
3
import
"fmt"
4
5
const
Hello
=
"Hi!"
6
var
world
string
7
8
func
init
()
{
9
world
=
"My world."
10
}
11
12
func
main
()
{
13
fmt
.
Println
(
Hello
,
world
)
14
}
The output is:
Hi
!
My
world
.
7.3 Formatting
A Go program can be formatted in (almost) any way the programmers want. With Go, let the machine take care of most formatting issues. The gofmt
program2 (also available as go fmt
, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments.
So if your program name is hello.go then format it as follows:
go
fmt
hello
.
go
The output of the filter gofmt
is the officially endorsed format.
The cool thing about Go code is that the officially endorsed format includes tabs, and so if you like having your tabs present as the traditional 8 spaces, go ahead. If you want them to present as 2 spaces, go ahead, if you want them to present as 4 spaces, or 20 spaces, you are not affecting anyone else with your preferences.
7.4 More on Strings
Strings3 are a sequence of UTF-8 characters (one-byte UTF-8 codes have the same value as the corresponding ASCII code). UTF-8 is the most widely used encoding, the standard encoding for text files, XML files and JSON strings. While able to represent characters that need 4 bytes, ASCII-characters are still stored using only 1 byte. A Go string is thus a sequence of variable-width characters (each 1 to 4 bytes), contrary to strings in other languages as C++, Java or Python that are fixed-width (Java uses always 2 bytes).
The advantages are that Go strings and text files occupy less memory/disk space, and since UTF-8 is the standard, Go does not need to encode and decode strings as other languages have to do. Strings are value types and immutable: once created you cannot modify the contents of the string.
A Go string is a sequence of variable-width characters where each character is represented by one or more bytes, normally using the UTF-8 encoding. Strings are length-delimited and not terminated by a special character as in C/C++.
The initial (default) value of a string is the empty string ""
.
String literals have type string
. String literals are of two types:
- Interpreted strings i.e. surrounded by
""
(double quotes). - Raw strings i.e. surrounded by back quotes.
They are not interpreted; they can span multiple lines. In the literal `This is a raw string \n
` the \n
is not interpreted but taken literally.
7.4.1 String functions
The standard library’s strings4 package provides many useful string-related functions. Here are a few in action:
1
package
main
2
3
import
"fmt"
4
import
s
"strings"
5
6
// We alias fmt.Println to a shorter name
7
var
p
=
fmt
.
Println
8
9
func
main
()
{
10
// Note that these are all functions from package, not methods on the
11
// string object itself. This means that we need pass the string in
12
// question as the first argument to the function.
13
p
(
"ToLower: "
,
s
.
ToLower
(
"TEST"
))
14
p
(
"ToUpper: "
,
s
.
ToUpper
(
"test"
))
15
p
(
"Split: "
,
s
.
Split
(
"a-b-c-d-e"
,
"-"
))
16
p
(
"Index: "
,
s
.
Index
(
"test"
,
"e"
))
17
p
(
"Contains: "
,
s
.
Contains
(
"test"
,
"es"
))
18
p
(
"Count: "
,
s
.
Count
(
"test"
,
"t"
))
19
}
The output is:
ToLower
:
test
ToUpper
:
TEST
Split
:
[
a
b
c
d
e
]
Index
:
1
Contains
:
true
Count
:
2
7.5 defer
Go has a special statement called defer
6 which schedules a function call to be run after the function completes.
defer
is often used when resources need to be freed in some way. For example when we open a file we need to make sure to close it later. With defer
:
1
file
,
err
:=
os
.
Open
(
"hello.go"
)
2
check
(
err
)
3
defer
file
.
Close
()
This has 3 advantages:
- it keeps our
Close
call near ourOpen
call so its easier to understand, - if our function had multiple return statements (perhaps one in an if and one in an else)
Close
will happen before both of them and - deferred functions are run even if a run-time error occurs.
Another example showing that defer
runs when the function returns and not when the scope is exited Also, that defer’ed function calls are run in LIFO order:
1
package
main
2
3
import
"fmt"
4
5
func
f
()
{
6
for
i
:=
0
;
i
<
5
;
i
++
{
7
defer
func
(
n
int
)
{
fmt
.
Println
(
"bye"
,
n
)
}(
i
)
8
fmt
.
Println
(
i
)
9
}
10
}
11
12
func
main
()
{
13
f
()
14
}
The output is:
0
1
2
3
4
bye
4
bye
3
bye
2
bye
1
bye
0
7.6 Using the log package
Usage: import "log"
Package log
implements a simple logging package. It defines a type, Logger
, with methods for formatting output. It also has a predefined ‘standard’ Logger accessible through helper functions Print[f|ln]
, Fatal[f|ln]
, and Panic[f|ln]
, which are easier to use than creating a Logger manually. That logger writes to standard error and prints the date and time of each logged message. The Fatal
functions call os.Exit(1)
after writing the log message. The Panic
functions call panic
after writing the log message.
Example: log.Fatal(err)
7.7 Using the big package
Package big
implements multi-precision arithmetic (big numbers). The following numeric types are supported:
-
Int
signed integers -
Rat
rational numbers
func NewInt(x int64) *Int
NewInt
allocates and returns a new Int
set to x.
func (z *Int) Mul(x, y *Int) *Int
Mul
sets z to the product x*y and returns z.
Here’s an example:
1
package
main
2
3
import
(
4
"fmt"
5
"math/big"
6
)
7
8
func
main
()
{
9
verybig
:=
big
.
NewInt
(
1
)
10
ten
:=
big
.
NewInt
(
10
)
11
12
for
i
:=
0
;
i
<
10000
;
i
++
{
13
temp
:=
new
(
big
.
Int
)
14
temp
.
Mul
(
verybig
,
ten
)
15
verybig
=
temp
16
}
17
18
fmt
.
Println
(
verybig
)
19
}
The output is:
1000.
....
Note: If you want it to run fast enough for Go Playground, use a smaller exponent than 100000.
7.8 go vet
go vet7 is used for analysing Go code and finding common mistakes. Among many it checks for some common mistakes like:
- Useless assignments
- Comparing functions with nil
- Using wrong printf format specifiers
- Closing over loop variables the wrong way
- Unreachable code
- Mistakes involving boolean operators
7.9 golint
Go is a tool8 for managing Go source code.
Usage: go command [arguments]
where get
is one of the many commands available.
go get
downloads and installs the packages named by the import paths, along with their dependencies.
Install golint
with go get github.com/golang/lint/golint
While go vet
checks your code for actual programming errors, golint
checks your code for style violations.
Golint does not emit errors or warnings, but “suggestions”: These suggestions can be wrong at times, and code that golint
complains about isn’t necessarily wrong – it might just be hitting a false positive. Nevertheless, it’s more often right than wrong, and you should definitely run golint
on your code from time to time and fix those suggestions that it is right about.
7.10 goimports
Install goimports
with go get golang.org/x/tools/cmd/goimports
Do you wish Go would just allow you to import unused packages? Or are you even tired of having to manually import all those packages you use? Then goimports
might be for you. It’s a replacement for gofmt
that, in addition to formatting your code, it also manages your imports for you.
It removes unused imports and adds missing imports.
Usage: goimports -w go_program.go
7.11 Naming Convention Summary
There is no detailed coding style guide for Golang.
Here are some references that would help:
Between gofmt
, golint
and go vet
, you cover already a lot of conventions.
Do read “Is there a Go programming style guide?”13
- https://golang.org/doc/effective_go.html#semicolons↩
- https://golang.org/doc/effective_go.html#formatting↩
- https://blog.golang.org/strings↩
- http://golang.org/pkg/strings/↩
- https://www.socketloop.com/tutorials/golang-reverse-a-string-with-unicode↩
- https://golang.org/doc/effective_go.html#defer↩
- https://golang.org/cmd/go/#hdr-Run_go_tool_vet_on_packages↩
- https://golang.org/cmd/go/↩
- https://github.com/golang/go/wiki/CodeReviewComments↩
- https://golang.org/doc/code.html↩
- http://blog.golang.org/godoc-documenting-go-code↩
- https://golang.org/doc/effective_go.html↩
- http://golang.org/doc/faq#Is_there_a_Go_programming_style_guide↩
8. Arrays
In Go, an array is a numbered sequence of elements of a specific length.
Arrays are easily understood with the help of this example:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
//exactly 5 ints. The type of elements
7
//and length are both part of the array's
8
//type. By default an array is zero-valued,
9
//and int types are by default initialized
10
//with a 0 value.
11
var
arr
[
5
]
int
12
fmt
.
Println
(
"arr:"
,
arr
)
13
14
//We can set a value at an index using the
15
//array[index] = value syntax, and get a
16
//value with array[index].
17
arr
[
3
]
=
10
18
fmt
.
Println
(
"set:"
,
arr
)
19
fmt
.
Println
(
"get:"
,
arr
[
3
])
20
21
//The builtin len returns the length of
22
//an array
23
fmt
.
Println
(
"len:"
,
len
(
arr
))
24
25
//Use this syntax to declare and initialize
26
//an array in one line.
27
arr2
:=
[
5
]
int
{
1
,
2
,
3
,
4
,
5
}
28
fmt
.
Println
(
"arr2:"
,
arr2
)
29
30
//multidimensional array
31
arr3
:=
[
2
][
2
]
int
{
{
1
,
2
},
{
3
,
4
}
}
32
fmt
.
Println
(
"arr3:"
,
arr3
)
33
}
The output is:
arr:
[
0
0
0
0
0
]
set:
[
0
0
0
10
0
]
get:
10
len:
5
arr2:
[
1
2
3
4
5
]
arr3:
[[
1
2
]
[
3
4
]]
Note that you can have the compiler count the array elements for you:
b := [...]string{"Golang", "Challenge"}
In both cases, the type of b is [2]string
.
Go’s arrays are values. An array variable denotes the entire array; it is not a pointer to the first array element (as would be the case in C). This means that when you assign or pass around an array value you will make a copy of its contents. (To avoid the copy you could pass a pointer to the array, but then that’s a pointer to an array.)
The in-memory representation of [4]int is just four integer values laid out sequentially:

9. Slices
Arrays are a bit inflexible, so you don’t see them too often in Go code. Slices, though, are everywhere. They build on arrays to provide great power and convenience.
A slice is a segment of an array. Like arrays, slices are indexable and have a length. Unlike arrays, this length is allowed to change. Here’s an example of a slice:
var x []int
The only difference between this and an array is the missing length between the brackets. In this case x
has been created with a length of 0
.
If you want to create a slice you should use the built-in make
function:
x := make([]int, 5)
This creates a slice that is associated with an underlying int
array of length 5. Slices are always associated with some array, and although they can never be longer than the array, they can be smaller or equal.
The make
function also allows a 3rd parameter:
x := make([]int, 5, 10)
10 represents the capacity of the underlying array which the slice points to. When the capacity argument is omitted, it defaults to the specified length.
The length and capacity of a slice can be inspected using the built-in len
and cap
functions.
len
(
x
)
==
5
cap
(
x
)
==
10
Another way to create slices is to use the [low : high]
expression:
1
arr
:=
[
5
]
int
{
1
,
2
,
3
,
4
,
5
}
2
x
:=
arr
[
0
:
5
]
low is the index of where to start the slice and high is the index where to end it (but not including the index itself). For example while arr[0:5]
returns [1,2,3,4,5]
, arr[1:4]
returns [2,3,4]
.
For convenience we are also allowed to omit low, high or even both low and high. arr[0:]
is the same as arr[0:len(arr)]
, arr[:5]
is the same as arr[0:5]
and arr[:]
is the same as arr[0:len(arr)]
.
9.1 Slice Functions
Go includes two built-in functions to assist with slices: append
and copy
. Here is an example of append
:
1
package
main
2
3
import
(
4
"fmt"
5
)
6
7
func
main
()
{
8
slice1
:=
[]
int
{
10
,
20
,
30
}
9
slice2
:=
append
(
slice1
,
40
,
50
)
10
fmt
.
Println
(
slice1
,
slice2
)
11
}
The output is:
[
10
20
30
]
[
10
20
30
40
50
]
append
creates a new slice by taking an existing slice (the first argument) and appending all the following arguments to it.
Here is an example of copy
:
1
package
main
2
3
import
(
4
"fmt"
5
)
6
7
func
main
()
{
8
slice1
:=
[]
int
{
10
,
20
,
30
}
9
slice2
:=
make
([]
int
,
2
)
10
copy
(
slice2
,
slice1
)
11
fmt
.
Println
(
slice1
,
slice2
)
12
}
The output is:
[
10
20
30
]
[
10
20
]
After running this program slice1
has [10,20,30]
and slice2
has [10,20]
. The contents of slice1
are copied into slice2
, but since slice2
has room for only two elements only the first two elements of slice1
are copied.
9.2 Slice internals
A slice is a descriptor of an array segment. It consists of a pointer to the array (ptr in image below), the length of the segment (len), and its capacity (cap - the maximum length of the segment).

Now s := make([]int, 5)
can be represented as:

The length is the number of elements referred to by the slice. The capacity is the number of elements in the underlying array (beginning at the element referred to by the slice pointer).
As we slice s, observe the changes in the slice data structure and their relation to the underlying array:
s = s[2:4]

Slicing does not copy the slice’s data. It creates a new slice value that points to the original array. This makes slice operations as efficient as manipulating array indices. Therefore, modifying the elements (not the slice itself) of a re-slice modifies the elements of the original slice.
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
arr
:=
[
5
]
int
{
1
,
2
,
3
,
4
,
5
}
7
slice
:=
arr
[
0
:
5
]
8
fmt
.
Println
(
slice
)
9
fmt
.
Println
(
"Len is: "
,
len
(
slice
))
10
fmt
.
Println
(
"Cap is: "
,
cap
(
slice
))
11
12
slice
=
slice
[
2
:
4
]
13
fmt
.
Println
(
slice
)
14
fmt
.
Println
(
"Len is: "
,
len
(
slice
))
15
fmt
.
Println
(
"Cap is: "
,
cap
(
slice
))
16
}
The output is:
[1 2 3 4 5]
Len
is:
5
Cap
is:
5
[3 4]
Len
is:
2
Cap
is:
3
Slicing does not copy the slice’s data. It creates a new slice value that points to the original array. This makes slice operations as efficient as manipulating array indices.
Slices provide a dynamic window onto the underlying array.
9.3 Passing a slice to a function
If you have a function which must operate on an array, create a slice reference and pass that to the function.
For example, here is a function that sums all elements in an array:
1
package
main
2
3
import
(
4
"fmt"
5
)
6
7
func
sum
(
a
[]
int
)
int
{
8
s
:=
0
9
for
i
:=
0
;
i
<
len
(
a
);
i
++
{
10
s
+=
a
[
i
]
11
}
12
return
s
13
}
14
15
func
main
()
{
16
var
arr
=
[
5
]
int
{
0
,
1
,
2
,
3
,
4
}
17
fmt
.
Println
(
"Sum is: "
,
sum
(
arr
[:]))
18
}
The output is:
Sum
is
:
10
10. Range
range iterates over elements in a variety of data structures.
If you’re looping over an array, slice, string, or map (more about this later), or reading from a channel (more about this later), a range
clause can manage the loop.
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
nums
:=
[]
int
{
2
,
3
,
4
}
7
sum
:=
0
8
for
key
,
value
:=
range
nums
{
9
sum
+=
value
10
fmt
.
Println
(
"key: "
,
key
,
" sum is: "
,
sum
)
11
}
12
fmt
.
Println
(
"Final sum:"
,
sum
)
13
}
The output is:
key
:
0
sum
is
:
2
key
:
1
sum
is
:
5
key
:
2
sum
is
:
9
Final
sum
:
9
If you only need the first item in the range (the key or index), drop the second:
1
for
key
:=
range
m
{
2
if
key
.
expired
()
{
3
delete
(
m
,
key
)
4
}
5
}
If you only need the second item in the range (the value), use the blank identifier, an underscore, to discard the first:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
nums
:=
[]
int
{
2
,
3
,
4
}
7
sum
:=
0
8
for
_
,
value
:=
range
nums
{
9
sum
+=
value
10
}
11
fmt
.
Println
(
"Final sum:"
,
sum
)
12
}
The output is:
Final sum: 9
The blank identifier _
as seen above, can be assigned or declared with any value of any type, with the value discarded harmlessly.
Here is a slighly bigger example:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
nums
:=
[]
int
{
2
,
3
,
4
}
7
8
//Here we use range to sum the
9
//numbers in a slice. Arrays work
10
//like this too.
11
sum
:=
0
12
for
_
,
num
:=
range
nums
{
13
sum
+=
num
14
}
15
fmt
.
Println
(
"sum:"
,
sum
)
16
17
//range on arrays and slices provides
18
//both the index and value for each entry.
19
//Above we didn't need the index, so we
20
//ignored it with the blank identifier _.
21
//Sometimes we actually want the indexes though.
22
for
i
,
num
:=
range
nums
{
23
if
num
==
3
{
24
fmt
.
Println
(
"index:"
,
i
)
25
}
26
}
27
}
The output is:
sum
:
9
index
:
1
You can also use range on strings directly. Then it will break out the individual Unicode characters and their start position, by parsing the UTF-8.
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
for
pos
,
char
:=
range
"a©b"
{
7
fmt
.
Printf
(
"character '%c' starts at byte position %d\n"
,
char
,
pos
)
8
}
9
}
The output is:
character
'a'
starts
at
byte
position
0
character
`
'©'
`
starts
at
byte
position
1
character
'b'
starts
at
byte
position
3
Observe that ‘b’ starts at position 3, which means '©'
took 2 bytes.
We had mentioned earlier that 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
. The Go language defines the word rune
as an alias for the type int32
.
A for range loop, by contrast, decodes one UTF-8-encoded rune on each iteration. Each time around the loop, the index of the loop is the starting position of the current rune, measured in bytes, and the code point is its value.
10.1 Variadic Functions using range
There is a special form available for the last parameter in a Go function. They are known as Variadic functions and can be called with any number of trailing arguments. By using ...
before the type name of the last parameter you can indicate that it takes zero or more of those parameters.
Here’s a function that will take an arbitrary number of ints as arguments:
1
package
main
2
3
import
"fmt"
4
5
func
add
(
args
...
int
)
int
{
6
total
:=
0
7
for
_
,
v
:=
range
args
{
8
total
+=
v
9
}
10
return
total
11
}
12
func
main
()
{
13
fmt
.
Println
(
add
(
1
,
2
))
14
fmt
.
Println
(
add
(
4
,
5
,
6
))
15
}
The output is:
3
15
11. Maps
A Go map is a built-in unordered collection of key-value pairs. Also known as an associative array, a hash table or a dictionary, maps are used to look up a value by its associated key.
To create an empty map
, use the built-in make
:
1
make
(
map
[
key
-
type
]
val
-
type
)
Note that the key can be an int
, a float
, complex number, string
, pointer
, even an interface
that supports equality. We’ll look at interfaces later. We can also have a map
like map[string]map[string]string
. This is a map of strings to maps of strings to strings.
Let us look at the following Go program:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
m
:=
make
(
map
[
string
]
int
)
7
8
//Set key/value pairs using typical
9
//name[key] = val syntax
10
m
[
"k1"
]
=
7
11
m
[
"k2"
]
=
13
12
13
//Printing a map with e.g. Println
14
//will show all of its key/value pairs
15
fmt
.
Println
(
"map:"
,
m
)
16
17
//Get a value for a key with name[key]
18
v1
:=
m
[
"k1"
]
19
fmt
.
Println
(
"v1: "
,
v1
)
20
21
//The builtin len returns the number of
22
//key/value pairs when called on a map
23
fmt
.
Println
(
"len:"
,
len
(
m
))
24
25
//The builtin delete removes key/value pairs
26
//from a map
27
delete
(
m
,
"k2"
)
28
fmt
.
Println
(
"map:"
,
m
)
29
30
//The optional second return value when getting
31
//a value from a map indicates if the key was
32
//present in the map. This can be used to
33
//disambiguate between missing keys and keys
34
//with zero values like 0 or ""
35
_
,
prs
:=
m
[
"k2"
]
36
fmt
.
Println
(
"prs:"
,
prs
)
37
38
//You can also declare and initialize a new map
39
//in the same line with this syntax
40
n
:=
map
[
string
]
int
{
"foo"
:
1
,
"bar"
:
2
}
41
fmt
.
Println
(
"map:"
,
n
)
42
}
The output of the program is:
map
:
map
[
k1
:
7
k2
:
13
]
v1
:
7
len
:
2
map
:
map
[
k1
:
7
]
prs
:
false
map
:
map
[
foo
:
1
bar
:
2
]
Note: x := make(map[int]int)
looks very much like an array but there are a few differences. First the length of a map (found by doing len(x)
) can change as we add new items to it. When first created it has a length of 0, after x[1] = 10
it has a length of 1. Second, maps are not sequential. We have x[1]
, and with an array that would imply there must be an x[0]
, but maps don’t have this requirement.
12. Structs
Based on what we have learnt so far, how would you write a program that calculates the area and perimeter of a rectangle?
Here’s what you would do:
1
package
main
2
3
import
"fmt"
4
5
func
Area
(
length
,
width
float64
)
float64
{
6
return
length
*
width
7
}
8
9
func
Perimeter
(
length
,
width
float64
)
float64
{
10
return
2
*
(
length
+
width
)
11
}
12
13
func
main
()
{
14
length
:=
4.5
15
width
:=
3.75
16
17
area
:=
Area
(
length
,
width
)
18
fmt
.
Println
(
"Area is = "
,
area
)
19
20
perimeter
:=
Perimeter
(
length
,
width
)
21
fmt
.
Println
(
"Perimeter is = "
,
perimeter
)
22
}
The output is:
Area
is
=
16.875
Perimeter
is
=
16.5
However, the way we have written the above program is more on the lines of procedural programming1. Let’s explore how to write the above program using Go’s struts but first understanding what structs in Go are.
Go allows you to define new types, it does this with the type
keyword:
type foo int
The above creates a new type foo which acts like an int
. Creating more sophisticated types is done with the struct
keyword.
A struct
is a collection of fields and is defined with the type
and struct
keywords.
An example:
1
type
myStructName
struct
{
2
x
int
3
y
string
4
}
The type
keyword introduces a new type. It is followed by the name of the type (myStructName
), the keyword struct
to indicate that we are defining a struct type and a list of fields inside of curly braces. Each field has a name and a type. Like with functions we can collapse fields that have the same type:
1
type
Circle
struct
{
2
x
,
y
,
r
float64
3
}
Struct fields are accessed using a dot. Here’s an example:
1
package
main
2
3
import
"fmt"
4
5
// this struct is visible only in this package
6
// as it starts with a small letter
7
type
myStructName
struct
{
8
// variables starts with small letter,
9
// so NOT visible outside the package
10
x
int
11
y
string
12
}
13
14
func
main
()
{
15
msn
:=
myStructName
{
1
,
"talim"
}
16
msn
.
x
=
4
17
fmt
.
Println
(
msn
.
x
)
18
}
The output is is 4
.
Let us define a Square struct as follows:
1
package
main
2
3
import
"fmt"
4
5
// this struct is visible outside this package
6
// as it starts with a capital letter
7
type
Square
struct
{
8
// variable X starts with a capital letter,
9
// so visible outside package
10
X
int
11
name
string
12
}
13
14
func
main
()
{
15
sqr
:=
Square
{}
16
fmt
.
Println
(
"Default square is: "
,
sqr
)
17
}
When you run the example, the output is Default square is: {0 }
This means that in a struct
the values of the variables by default for an int
will be 0; a string
will be empty, etc.
Now let us look at different ways of initializing a struct
and setting values for variables within it.
1
package
main
2
3
import
"fmt"
4
5
type
Square
struct
{
6
X
int
7
name
string
8
}
9
10
func
main
()
{
11
//initialize values in order they are defined in struct
12
sqr1
:=
Square
{
10
,
"Go_Sqr1"
}
13
fmt
.
Println
(
"Square sqr1 is : "
,
sqr1
)
14
15
//initialize values by variable name in any order
16
sqr2
:=
Square
{
name
:
"Go_Sqr2"
,
X
:
15
}
17
fmt
.
Println
(
"Square sqr2 is : "
,
sqr2
)
18
19
//get pointer to an instance with new keyword
20
ps
:=
new
(
Square
)
21
22
//set value using . notation by dereferencing pointer
23
(
*
ps
).
X
=
30
24
25
//set value using . same as above
26
ps
.
name
=
"Go_Sqr3"
27
28
fmt
.
Println
(
"Square ps is : "
,
*
ps
)
29
30
ps1
:=
&
sqr1
31
ps1
.
X
=
22
32
fmt
.
Println
(
"The new value of X: "
,
ps1
.
X
)
33
fmt
.
Println
(
"Square sqr1 is : "
,
sqr1
)
34
}
The output is:
Square
sqr1
is
:
{
10
Go_Sqr1
}
Square
sqr2
is
:
{
15
Go_Sqr2
}
Square
ps
is
:
{
30
Go_Sqr3
}
The
new
value
of
X
:
22
Square
sqr1
is
:
{
22
Go_Sqr1
}
Note: As seen above, you can get a pointer to a newly created struct
instance using the new
keyword. A pointer thus obtained, can be used with or without using the *
operator to get variables within it.
Thus new(T)
returns a pointer to a newly allocated zero value of type T. This is important to remember.
This means a user of the data structure can create one with new and get right to work.
Another example:
1
package
main
2
3
import
"fmt"
4
5
type
Person
struct
{
6
name
string
7
age
int
8
}
9
10
func
main
()
{
11
fmt
.
Println
(
Person
{
"Kiran"
,
30
})
12
fmt
.
Println
(
Person
{
name
:
"Satish"
,
age
:
60
})
13
14
s
:=
Person
{
"Victor"
,
45
}
15
fmt
.
Println
(
s
)
16
17
r
:=
&
Person
{
"Harry"
,
30
}
// Pointer
18
fmt
.
Println
(
r
)
19
r
.
age
=
33
// Changing the values
20
fmt
.
Println
(
*
r
)
// Pointer de-referencing
21
22
// Another example of Pointers
23
p
:=
&
Person
{}
24
p
.
name
=
"Hulk"
25
p
.
age
=
10
26
fmt
.
Println
(
*
p
)
27
}
The output is:
{
Kiran
30
}
{
Satish
60
}
{
Victor
45
}
&
{
Harry
30
}
{
Harry
33
}
{
Hulk
10
}
An anonymous struct field
1
package
main
2
3
import
"fmt"
4
5
type
A
struct
{
6
ax
,
ay
int
7
}
8
type
B
struct
{
9
A
10
bx
,
by
float64
11
}
12
13
func
main
()
{
14
b
:=
B
{
A
{
1
,
2
},
3.0
,
4.0
}
15
fmt
.
Println
(
b
.
ax
,
b
.
ay
,
b
.
bx
,
b
.
by
)
16
fmt
.
Println
(
b
.
A
)
17
}
The output is:
1
2
3
4
{
1
2
}
Anonymous fields of any type
Any named type, or pointer to one, may be used in an anonymous field and it may appear at any location in the struct.
1
package
main
2
3
import
"fmt"
4
5
type
C
struct
{
6
x
float64
7
int
8
string
9
}
10
11
func
main
()
{
12
c
:=
C
{
3.5
,
7
,
"hello"
}
13
fmt
.
Println
(
c
.
x
,
c
.
int
,
c
.
string
)
14
}
The output is:
3.5
7
hello
Now let us understand how to define a function on a struct
.
A normal function that we name myFunc
that takes no parameters and returns an int
would be defined similar to the code snippet shown below:
1
func
myFunc
()
int
{
2
//code
3
}
A function like myFunc
that takes no parameters and returns an int
, but which is associated with a type
which we call as myType
would be defined similar to the code snippet shown below:
1
type
myType
struct
{}
2
3
func
(
f
myType
)
myFunc
()
int
{
4
//code
5
}
In between the keyword func
and the name of the function (myFunc()
) we have added a “receiver”. The receiver is like a parameter – it has a name and a type. In Go, a function which takes a receiver is usually called a method.
Let’s extend our earlier Square
struct
to add an Area
function. This time we will define that the Area
function works explicitly with the Square
type:
1
package
main
2
3
import
"fmt"
4
5
type
Square
struct
{
6
X
int
7
name
string
8
}
9
10
func
(
s
Square
)
Area
()
int
{
11
return
s
.
X
*
s
.
X
12
}
13
14
func
main
()
{
15
r1
:=
Square
{
5
,
"my_sqr"
}
16
fmt
.
Println
(
"Square is: "
,
r1
)
17
fmt
.
Println
(
"Square area is: "
,
r1
.
Area
())
18
}
The output is:
Square
is
:
{
5
my_sqr
}
Square
area
is
:
25
Let’s now modify the program stuct0.go
to use structs.
1
package
main
2
3
import
"fmt"
4
5
type
Rectangle
struct
{
6
length
,
width
float64
7
}
8
9
func
(
r
Rectangle
)
Area
()
float64
{
10
return
r
.
length
*
r
.
width
11
}
12
13
func
(
r
Rectangle
)
Perimeter
()
float64
{
14
return
2
*
(
r
.
length
+
r
.
width
)
15
}
16
17
func
main
()
{
18
r
:=
Rectangle
{
4.5
,
3.75
}
19
20
area
:=
r
.
Area
()
21
fmt
.
Println
(
"Area is = "
,
area
)
22
23
perimeter
:=
r
.
Perimeter
()
24
fmt
.
Println
(
"Perimeter is = "
,
perimeter
)
25
}
The output is:
Area
is
=
16.875
Perimeter
is
=
16.5
12.1 What’s the difference between pointer and non-pointer method receivers?
We know that in Go, a function which takes a receiver is usually called a method.
func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct) valueMethod() { } // method on value
Simply stated: you can treat the receiver as if it was an argument being passed to the method. All the same reasons why you might want to pass by value or pass by reference apply.
Reasons why you would want to pass by reference as opposed to by value:
- You want to actually modify the receiver (“read/write” as opposed to just “read”)
- The
struct
is very large and a deep copy is expensive - Consistency: if some of the methods on the
struct
have pointer receivers, the rest should too. This allows predictability of behavior
If you need these characteristics on your method call, use a pointer receiver.
Here are even more rules of thumb2 to help you choose whether to use a value or pointer receiver.
1
package
main
2
3
import
"fmt"
4
5
type
Number
struct
{
6
i
int
7
j
int
8
}
9
10
func
(
n
Number
)
Same
()
{
11
n
.
i
=
23
12
n
.
j
=
47
13
}
14
15
func
(
n
*
Number
)
Change
()
{
16
n
.
i
=
23
17
n
.
j
=
47
18
}
19
20
func
main
()
{
21
n
:=
&
Number
{
0
,
0
}
22
fmt
.
Println
(
n
)
23
n
.
Same
()
24
fmt
.
Println
(
n
)
25
n
.
Change
()
26
fmt
.
Println
(
n
)
27
}
The output is:
&
{
0
0
}
&
{
0
0
}
&
{
23
47
}
The method Same
is defined with a non-pointer receiver and doesn’t change the values of the struct
it is invoked on, and Change
is defined with a pointer receiver, so it does change the values of the struct
upon which it is invoked.
12.2 Solutions
13. interface
An interface is a set of methods. To turn it around, the methods implemented by a concrete type such as a struct
form the interface of that type.
Let’s focus on the method set aspect of interfaces first.
In Go, suppose you have an interface for a Dog:
1
type
Dog
interface
{
2
Woof
()
3
}
Any struct that has a Woof method will implement the Dog interface implicitly and can be used as a Dog.
1
package
main
2
3
import
"fmt"
4
5
type
Dog
interface
{
6
Woof
()
7
}
8
9
type
Lassie
struct
{
10
}
11
12
func
(
l
Lassie
)
Woof
()
{
13
fmt
.
Println
(
"woof woof!"
)
14
}
15
16
type
Astro
struct
{
17
}
18
19
func
(
a
Astro
)
Woof
()
{
20
fmt
.
Println
(
"WOOF WOOF!!"
)
21
}
22
23
func
main
()
{
24
var
dog
Dog
25
dog
=
Lassie
{}
26
dog
.
Woof
()
27
28
dog
=
Astro
{}
29
dog
.
Woof
()
30
}
with output:
woof
woof
!
WOOF
WOOF
!!
If you have another struct, Cat, which doesn’t have a Woof method and you tried to use it as a Dog then you’ll get a compile time error:
1
package
main
2
3
import
"fmt"
4
5
type
Dog
interface
{
6
Woof
()
7
}
8
9
type
Lassie
struct
{
10
}
11
12
func
(
l
Lassie
)
Woof
()
{
13
fmt
.
Println
(
"woof woof!"
)
14
}
15
16
type
Astro
struct
{
17
}
18
19
func
(
a
Astro
)
Woof
()
{
20
fmt
.
Println
(
"WOOF WOOF!!"
)
21
}
22
23
type
Cat
struct
{
24
}
25
26
func
(
c
Cat
)
Meow
()
{
27
fmt
.
Println
(
"meow meow"
)
28
}
29
30
func
main
()
{
31
var
dog
Dog
32
dog
=
Lassie
{}
33
dog
.
Woof
()
34
35
dog
=
Astro
{}
36
dog
.
Woof
()
37
38
dog
=
Cat
{}
39
dog
.
Woof
()
40
}
with output:
cannot
use
Cat
literal
(
type
Cat
)
as
type
Dog
in
assignment
:
Cat
does
not
implement
Dog
(
missing
Woof
method
)
Earlier we had seen an example of a Square
struct
and an Area
function:
1
package
main
2
3
import
"fmt"
4
5
type
Square
struct
{
6
Width
,
Height
int
7
}
8
9
func
(
s
Square
)
Area
()
int
{
10
return
s
.
Width
*
s
.
Height
11
}
12
13
func
main
()
{
14
r1
:=
Square
{
5
,
5
}
15
fmt
.
Println
(
"Square is: "
,
r1
)
16
fmt
.
Println
(
"Square area is: "
,
r1
.
Area
())
17
}
with output:
Square
is
:
{
5
5
}
Square
area
is
:
25
Note: Although not used here, it is idiomatic in Go for interface names to be suffixed with “-er” (like Positioner, Distancer).
Let us now abstract out the Area
function into an interface
called Geometry
. Geometry
is an interface
and has a single function Area
that returns an int
as follows:
1
type
Geometry
interface
{
2
Area
()
int
3
}
Let us write the full program:
1
package
main
2
3
import
"fmt"
4
5
type
Square
struct
{
6
Width
,
Height
int
7
}
8
9
type
Geometry
interface
{
10
Area
()
int
11
}
12
13
// To implement an interface in Go, we just need to implement all the methods
14
// in the interface
15
func
(
s
Square
)
Area
()
int
{
16
return
s
.
Width
*
s
.
Height
17
}
18
19
// If a variable has an interface type, then we can call methods that are in
20
// the named interface. Here is a generic measure function taking advantage of
21
// this to work on any Geometry.
22
func
measure
(
g
Geometry
)
{
23
fmt
.
Println
(
g
)
24
fmt
.
Println
(
g
.
Area
())
25
}
26
27
func
main
()
{
28
s
:=
Square
{
Width
:
3
,
Height
:
4
}
29
30
// The Square struct type implements the Geometry interface so we can
31
// use instances of these structs as arguments to measure.
32
measure
(
s
)
33
}
The output is:
{
3
4
}
12
Here’s an example1 of a simple Animal interface.
13.1 The interface{} type
It’s possible to define an interface
without any methods. Such an interface
is known as an empty interface, and it’s denoted by interface{}
. Since there are no methods, all types will satisfy this interface.
That means if you write a function that takes an interface{}
value as a parameter, you can supply that function with any value. So, this function:
1
func
DoSomething
(
v
interface
{})
{
2
// ...
3
}
will accept any parameter whatsoever.
Here’s where it gets confusing: inside of the DoSomething
function, what is v’s type? Beginners are led to believe that “v is of any type”, but that is wrong. v
is not of any type; it is of interface{}
type. Wait, what? When passing a value into the DoSomething
function, the Go runtime will perform a type conversion (if necessary), and convert the value to an interface{}
value. All values have exactly one type at runtime, and v’s one static type is interface{}
.
This should leave you wondering: ok, so if a conversion is taking place, what is actually being passed into a function that takes an interface{}
value?
If you understand that an interface
value is two words wide and it contains a pointer to the underlying data, that’s typically enough to avoid common pitfalls.
This topic is adapted from blog posts this2 and this3
13.2 Solutions
14. Error
It is idiomatic in Go to use the error
interface type as the return type for any error that is going to be returned from a function or method. This interface is used by all the functions and methods in the standard library that return errors.
Go code uses error
values to indicate an abnormal state. For example, the os.Open
function returns a non-nil error value when it fails to open a file.
func Open(name string) (file *File, err error)
The following code uses os.Open
to open a file. If an error occurs it calls log.Fatal
to print the error message and stop.
1
f
,
err
:=
os
.
Open
(
"filename.ext"
)
2
if
err
!=
nil
{
3
log
.
Fatal
(
err
)
4
}
5
// do something with the open *File f
Handling errors that are returned from functions and methods starts by checking if the returned interface value of type error
is not nil
. Thus in the above code, the value of the err
variable is compared to the value of nil
. If the value is not nil, then there was an error.
In Go, error handling is important. The language’s design and conventions encourage you to explicitly check for errors where they occur (as distinct from the convention in other languages of throwing exceptions and sometimes catching them). In some cases this makes Go code verbose.
15. io, os and net/http - Getting started
15.1 io
Usage: import "io"
Package io
provides basic interfaces to I/O primitives.
func Copy(dst Writer, src Reader) (written int64, err error)
Copy
copies from src to dst until either EOF
is reached on src or an error occurs. It returns the number of bytes copied and the first error encountered while copying, if any.
A successful Copy
returns err == nil
, not err == EOF
. Because Copy
is defined to read from src until EOF, it does not treat an EOF from Read as an error to be reported.
15.2 os
Usage: import "os"
Package os
provides a platform-independent interface to operating system functionality.
Stdin
, Stdout
, and Stderr
are open Files pointing to the standard input, standard output, and standard error file descriptors.
Hence os.Stdout
will write to the standard output.
15.3 TCP and net/http
Networking usually implies TCP/IP, the way in which millions of machines communicate back and forth across the Internet. Network communication is conceptualized at different layers - data link, network, transport and application. The application layer is the world of telnet, FTP, email protocols, and much more.
15.3.1 Basic Networking
Let us talk a little bit about basic networking.
Our discussion of networking focuses on both sides of a client-server relationship. The client requests that some action be performed, and the server performs the action and responds to the client. A dedicated server spends its lifetime waiting for messages and answering them. A common implementation of the request-response model is between World Wide Web browsers and World Wide Web servers. When a user selects a Web site to browse through a browser (the client application), a request is sent to the appropriate Web server (the server application). The server normally responds to the client by sending an appropriate HTML Web page.
15.3.2 Port
A port is not a physical device, but an abstraction to facilitate communication between a server and a client.
Ports are described by a 16-bit integer value. Hence, a machine can have a maximum of 65536 port numbers (ranging from 0 to 65535). The port numbers are divided into three ranges: the Well Known Ports, the Registered Ports, and the Dynamic and/or Private Ports. The Well Known Ports are those from 0 through 1023 (for example, port no. 80 is for http, port no. 25 is for smtp and so on). The Registered Ports are those from 1024 through 49151. The Dynamic and/or Private Ports are those from 49152 through 65535.
15.3.3 Internet Addresses
These are the numerical host addresses that consist of four bytes such as 132.163.4.102. The IP address 127.0.0.1 (localhost) is a special address, called the local loopback address, which denotes the local machine.
15.3.4 net/http
Usage: import "net/http"
Package http
provides HTTP client and server implementations.
Get
, Head
, Post
, and PostForm
make HTTP (or HTTPS) requests.
func Get(url string) (resp *Response, err error)
Get
issues a GET
to the specified URL. An error
is returned if there was an HTTP protocol error. When err
is nil, resp
always contains a non-nil resp.Body
. Caller should close resp.Body
when done reading from it.
The Response2 structure among others has the field StatusCode
which is an int
(e.g. 200), a field Body
that represents the response body and Close
which should be called by the caller to close the Body.
The http.StatusOK
is a constant with the value of 200.
An example:
1
package
main
2
3
import
(
4
"io"
5
"log"
6
"net/http"
7
"os"
8
)
9
10
func
main
()
{
11
resp
,
err
:=
http
.
Get
(
"http://www.google.com/robots.txt"
)
12
if
err
!=
nil
{
13
log
.
Fatal
(
err
)
14
}
15
16
defer
resp
.
Body
.
Close
()
17
18
if
resp
.
StatusCode
!=
http
.
StatusOK
{
19
log
.
Fatal
(
resp
.
Status
)
20
}
21
22
_
,
err
=
io
.
Copy
(
os
.
Stdout
,
resp
.
Body
)
23
if
err
!=
nil
{
24
log
.
Fatal
(
err
)
25
}
26
}
16. Random Numbers
Go’s math/rand package provides pseudo-random number generation.
Random numbers are generated by a Source. Top-level functions, such as Float64
and Int
, use a default shared Source that produces a deterministic sequence of values each time a program is run. Use the Seed
function to initialize the default Source if different behavior is required for each run. The default Source is safe for concurrent use by multiple goroutines.
Usage: import "math/rand"
func Intn(n int) int
Intn
returns, as an int
, a non-negative pseudo-random number in [0,n) from the default Source. It panics if n <= 0.
func Float64() float64
Float64
returns, as a float64
, a pseudo-random number in [0.0,1.0) from the default Source.
Let us look at an example:
1
package
main
2
3
import
(
4
"fmt"
5
"math/rand"
6
)
7
8
func
main
()
{
9
fmt
.
Println
(
rand
.
Intn
(
500
))
10
fmt
.
Println
(
rand
.
Float64
())
11
}
In the above program, rand.Intn
returns a random int n, 0 <= n < 500.
When you run the above program the output is always the same. I got 81
and 0.9405090880450124 no matter how many times I ran the program.
To make the pseudo-random generator deterministic, give it a well-known seed. To do that let us look at:
func NewSource(seed int64) Source
NewSource
returns a new pseudo-random Source
seeded with the given value.
Let us modify our above program as:
1
package
main
2
3
import
(
4
"fmt"
5
"math/rand"
6
)
7
8
func
main
()
{
9
fmt
.
Print
(
rand
.
Intn
(
500
))
10
fmt
.
Println
(
rand
.
Float64
())
11
12
s1
:=
rand
.
NewSource
(
21
)
13
r1
:=
rand
.
New
(
s1
)
14
fmt
.
Println
(
r1
.
Intn
(
500
))
15
fmt
.
Println
(
r1
.
Float64
())
16
}
Now the output I get is always:
810.9405090880450124
3000.8375676569149538
17. File Handling
17.1 Package ioutil
Usage: import "io/ioutil"
The function ioutil.ReadFile(filename string)
reads the entire contents of the file filename
into memory.
Type and store the following program in the same folder where you had written hello.go
:
1
package
main
2
3
import
(
4
"fmt"
5
"io/ioutil"
6
"log"
7
)
8
9
func
check
(
e
error
)
{
10
if
e
!=
nil
{
11
log
.
Fatal
(
e
)
12
}
13
}
14
15
func
main
()
{
16
dat
,
err
:=
ioutil
.
ReadFile
(
"hello.go"
)
17
check
(
err
)
18
fmt
.
Print
(
string
(
dat
))
19
}
The program will output the contents of the file hello.go
.
func ReadFile(filename string) ([]byte, error)
ReadFile
reads the file named by filename and returns the contents. A successful call returns err == nil
, not err == EOF
. Because ReadFile
reads the whole file, it does not treat an EOF from Read as an error to be reported.
17.2 Package os
Usage: import "os"
Documentation: http://golang.org/pkg/os/
Package os
provides a platform-independent interface to operating system functionality.
Let us look at some programs in this section.
17.2.1 read_file_os1.go
First write the program read_file_os1.go
in the same folder where you had written hello.go
as follows:
1
package
main
2
3
import
(
4
"fmt"
5
"log"
6
"os"
7
)
8
9
func
check
(
e
error
)
{
10
if
e
!=
nil
{
11
log
.
Fatal
(
e
)
12
}
13
}
14
15
func
main
()
{
16
file
,
err
:=
os
.
Open
(
"hello.go"
)
// For read access
17
check
(
err
)
18
defer
file
.
Close
()
19
20
// get the file size
21
stat
,
err
:=
file
.
Stat
()
22
check
(
err
)
23
24
// read the file
25
bs
:=
make
([]
byte
,
stat
.
Size
())
26
_
,
err
=
file
.
Read
(
bs
)
27
check
(
err
)
28
29
str
:=
string
(
bs
)
// type string
30
fmt
.
Println
(
str
)
31
}
The above program is an example of how to read the contents of a file and display them on the terminal.
17.2.1.1 Stat
1
func
(
f
*
File
)
Stat
()
(
fi
FileInfo
,
err
error
)
Stat
returns the FileInfo
structure describing file. If there is an error, it will be of type *PathError
.
17.2.1.2 FileInfo
A FileInfo
describes a file and is returned by Stat
and Lstat
.
Here’s the FileInfo
interface:
1
type
FileInfo
interface
{
2
Name
()
string
// base name of the file
3
Size
()
int64
// length in bytes for regular files;
4
// system-dependent for others
5
Mode
()
FileMode
// file mode bits
6
ModTime
()
time
.
Time
// modification time
7
IsDir
()
bool
// abbreviation for Mode().IsDir()
8
Sys
()
interface
{}
// underlying data source (can return nil)
9
}
Observe that Size()
gives us the length in bytes for regular files.
17.2.2 read_file_os2.go
Next write the second program read_file_os2.go
in the same folder where you had written hello.go
as follows:
1
package
main
2
3
import
(
4
"fmt"
5
"log"
6
"os"
7
)
8
9
func
check
(
e
error
)
{
10
if
e
!=
nil
{
11
log
.
Fatal
(
e
)
12
}
13
}
14
15
func
main
()
{
16
file
,
err
:=
os
.
Open
(
"hello.go"
)
// For read access
17
check
(
err
)
18
defer
file
.
Close
()
19
20
//The file's data can then be read into a slice
21
//of bytes. Read and Write take their byte counts
22
//from the length of the argument slice.
23
//Read some bytes from the beginning of
24
//the file. Allow up to 5 to be read but
25
//also note how many actually were read.
26
slice1
:=
make
([]
byte
,
5
)
27
n1
,
err
:=
file
.
Read
(
slice1
)
28
check
(
err
)
29
fmt
.
Printf
(
"%d bytes: %s\n"
,
n1
,
string
(
slice1
))
30
31
//You can also Seek to a known location in
32
//the file and Read from there.
33
o2
,
err
:=
file
.
Seek
(
6
,
0
)
34
check
(
err
)
35
slice2
:=
make
([]
byte
,
2
)
36
n2
,
err
:=
file
.
Read
(
slice2
)
37
check
(
err
)
38
fmt
.
Printf
(
"%d bytes @ %d: %s\n"
,
n2
,
o2
,
string
(
slice2
))
39
}
Part of the output is shown below:
5
bytes
:
// Si
2
bytes
@
6
:
gl
17.2.2.1 seek function:
1
func
(
f
*
File
)
Seek
(
offset
int64
,
whence
int
)
(
ret
int64
,
err
error
)
Seek sets the offset for the next Read or Write on file to offset, interpreted according to whence: 0 means relative to the origin of the file, 1 means relative to the current offset, and 2 means relative to the end. It returns the new offset and an error, if any.
Note: There is no built-in rewind, but Seek(0, 0)
accomplishes this.
17.2.3 write_file_os.go
The next program write_file_os.go
is an example on how we can create a file:
1
package
main
2
3
import
(
4
"log"
5
"os"
6
)
7
8
func
check
(
e
error
)
{
9
if
e
!=
nil
{
10
log
.
Fatal
(
e
)
11
}
12
}
13
14
func
main
()
{
15
file
,
err
:=
os
.
Create
(
"test.txt"
)
16
check
(
err
)
17
defer
file
.
Close
()
18
19
file
.
WriteString
(
"test"
)
20
21
//Issue a Sync to flush writes
22
//to stable storage.
23
file
.
Sync
()
24
}
17.2.3.1 Create
1
func
Create
(
name
string
)
(
file
*
File
,
err
error
)
Create
creates the named file mode 0666 (before umask), truncating it if it already exists. If successful, methods on the returned File
can be used for I/O; the associated file descriptor has mode O_RDWR. If there is an error, it will be of type *PathError
.
17.2.3.2 WriteString
1
func
(
f
*
File
)
WriteString
(
s
string
)
(
ret
int
,
err
error
)
WriteString
writes the contents of string s.
17.2.4 read_dir_os.go
The next program read_dir_os.go
is an example on how to get the contents of a directory:
1
package
main
2
3
import
(
4
"fmt"
5
"log"
6
"os"
7
)
8
9
func
check
(
e
error
)
{
10
if
e
!=
nil
{
11
log
.
Fatal
(
e
)
12
}
13
}
14
15
func
main
()
{
16
dir
,
err
:=
os
.
Open
(
"."
)
17
check
(
err
)
18
defer
dir
.
Close
()
19
20
fileInfos
,
err
:=
dir
.
Readdir
(
-
1
)
21
check
(
err
)
22
for
_
,
fi
:=
range
fileInfos
{
23
fmt
.
Println
(
fi
.
Name
())
24
}
25
}
17.2.4.1 Readdir
1
func
(
f
*
File
)
Readdir
(
n
int
)
(
fi
[]
FileInfo
,
err
error
)
Readdir
reads the contents of the directory associated with file and returns a slice of up to n ``FileInfo values, in directory order. Subsequent calls on the same file will yield further FileInfos
.
If n > 0, Readdir
returns at most n FileInfo
structures. In this case, if Readdir
returns an empty slice, it will return a non-nil error explaining why. At the end of a directory, the error is io.EOF
.
If n <= 0, Readdir
returns all the FileInfo
from the directory in a single slice. In this case, if Readdir
succeeds (reads all the way to the end of the directory), it returns the slice and a nil error. If it encounters an error before the end of the directory, Readdir
returns the FileInfo
read until that point and a non-nil error.
17.2.4.2 Name()
Name()
returns a string which is the base name of the file.
17.3 Package bufio
Usage: import "bufio"
Documentation: bufio
Package bufio
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.
Let us write a program read_bufio.go
as follows:
1
package
main
2
3
import
(
4
"bufio"
5
"fmt"
6
"log"
7
"os"
8
)
9
10
func
check
(
e
error
)
{
11
if
e
!=
nil
{
12
log
.
Fatal
(
e
)
13
}
14
}
15
16
func
main
()
{
17
file
,
err
:=
os
.
Open
(
"hello.go"
)
18
check
(
err
)
19
defer
file
.
Close
()
20
21
reader
:=
bufio
.
NewReader
(
file
)
22
23
b4
,
err
:=
reader
.
Peek
(
5
)
24
check
(
err
)
25
fmt
.
Printf
(
"5 bytes: %s\n"
,
string
(
b4
))
26
}
17.3.1 NewReader
1
func
NewReader
(
rd
io
.
Reader
)
*
Reader
NewReader
returns a new Reader
whose buffer has the default size.
17.3.2 Peek
1
func
(
b
*
Reader
)
Peek
(
n
int
)
([]
byte
,
error
)
Peek
returns the next n bytes without advancing the reader. The bytes stop being valid at the next read call. If Peek
returns fewer than n bytes, it also returns an error explaining why the read is short. The error is ErrBufferFull
if n is larger than b’s buffer size.
Now let us write a program write_bufio.go
as follows:
1
package
main
2
3
import
(
4
"bufio"
5
"fmt"
6
"log"
7
"os"
8
)
9
10
func
check
(
e
error
)
{
11
if
e
!=
nil
{
12
log
.
Fatal
(
e
)
13
}
14
}
15
16
func
main
()
{
17
f
,
err
:=
os
.
Create
(
"test2.txt"
)
18
check
(
err
)
19
defer
f
.
Close
()
20
21
w
:=
bufio
.
NewWriter
(
f
)
22
n4
,
err
:=
w
.
WriteString
(
"buffered\n"
)
23
fmt
.
Printf
(
"wrote %d bytes\n"
,
n4
)
24
25
w
.
Flush
()
26
}
The output is:
wrote
9
bytes
17.3.3 NewWriter
1
func
NewWriter
(
wr
io
.
Writer
)
*
Writer
NewWriter
returns a new Writer
whose buffer has the default size.
Original file contents:
text
text
text
text
text
text
text
text
text
text
text
text
word
text
text
text
text
text
text
text
text
text
text
text
text
Modified file contents:
text
text
text
text
text
text
text
text
text
text
text
text
inserted
word
text
text
text
text
text
text
text
text
text
text
text
text
17.4 Solutions
Exercise 17 Solution:
Exercise 18 solution:

WAIT… If you have liked the course so far, I am confident that you will like the latter part of the course too. This is where the fun begins.
Glad to have you back…
18. JSON and Go
18.1 JSON
JSON stands for JavaScript Object Notation.
JSON is syntax for storing and exchanging text information, much like XML. JSON is smaller than XML, and faster and easier to parse. JSON is language independent. Here’s an example:
1
{
2
"employees"
:
[
3
{
"firstName"
:
"John"
,
"lastName"
:
"Doe"
},
4
{
"firstName"
:
"Anna"
,
"lastName"
:
"Smith"
},
5
{
"firstName"
:
"Peter"
,
"lastName"
:
"Jones"
}
6
]
7
}
The employee object is an array of 3 employee records (objects).
JSON data is written as name/value pairs. A name/value pair consists of a field name (in double quotes), followed by a colon and followed by a value:
"firstName"
:
"Satish"
JSON values can be:
- A number (integer or floating point)
- A string (in double quotes)
- A Boolean (true or false)
- An array (in square brackets)
- An object (in curly brackets)
- null
JSON objects are written inside curly brackets, Objects can contain multiple name/values pairs:
{
"firstName"
:
"Satish"
,
"lastName"
:
"Talim"
}
18.2 Package json
Usage: import "encoding/json"
Package json
implements encoding and decoding of JSON objects.
18.2.1 Encoding
To encode JSON data we use the Marshal
function.
func Marshal(v interface{}) ([]byte, error)
Let’s look at an example.
1
package
main
2
3
import
(
4
"encoding/json"
5
"fmt"
6
)
7
8
type
User
struct
{
9
UserName
string
10
EmailID
string
11
Password
string
12
}
13
14
func
main
()
{
15
u
:=
User
{
UserName
:
"IndianGuru"
,
EmailID
:
"satishtalim@gmail.com"
,
Password
:
"password"
}
16
m
,
_
:=
json
.
Marshal
(
u
)
17
fmt
.
Println
(
string
(
m
))
18
}
The output is:
{
"UserName"
:
"IndianGuru"
,
"EmailID"
:
"satishtalim@gmail.com"
,
"Password"
:
"password"
}
In the above program we have a simple struct
, we create a new instance of this struct
and encode it.
18.2.2 Struct tags
1
type
Person
struct
{
2
UserName
string
`json:"user_name"`
3
EmailID
string
4
Password
string
`json:"-"`
5
}
When we use the Marshal
function on a struct
instance it produces JSON.
In the above example, a field appears in JSON as key “user_name” and the Password field is ignored by this package.
In the previous example json1.go
the password got printed. However, we don’t want to print the password. Here’s the changed code:
1
package
main
2
3
import
(
4
"encoding/json"
5
"fmt"
6
)
7
8
type
User
struct
{
9
UserName
string
`json:"user_name"`
10
EmailID
string
11
Password
string
`json:"-"`
12
}
13
14
func
main
()
{
15
u
:=
User
{
UserName
:
"IndianGuru"
,
EmailID
:
"satishtalim@gmail.com"
,
Password
:
"password"
}
16
m
,
_
:=
json
.
Marshal
(
u
)
17
fmt
.
Println
(
string
(
m
))
18
}
The output is:
{
"user_name"
:
"IndianGuru"
,
"EmailID"
:
"satishtalim@gmail.com"
}
What if I don’t want to show the UserName if it is empty? Here’s the modified code:
1
package
main
2
3
import
(
4
"encoding/json"
5
"fmt"
6
)
7
8
type
User
struct
{
9
UserName
string
`json:"first_name,omitempty"`
10
EmailID
string
11
Password
string
`json:"-"`
12
}
13
14
func
main
()
{
15
u
:=
User
{
UserName
:
""
,
EmailID
:
""
,
Password
:
"password"
}
16
m
,
_
:=
json
.
Marshal
(
u
)
17
fmt
.
Println
(
string
(
m
))
18
}
The output is:
{
"EmailID"
:
""
}
The JSON parser also accepts a flag in the tag to let it know what to do if the field is empty. The omitempty
flag tells it to not include the JSON value in the output if it’s the “zero-value” for that type.
The “zero-value” for numbers is 0, for strings it’s the empty string, for maps, slices and pointers it’s nil. This is how you include the omitempty
flag.
1
type
MyStruct
struct
{
2
SomeField
string
`json:"some_field,omitempty"`
3
}
Notice that the flag goes inside the quotes.
If the SomeField was an empty string, and you converted it to JSON, some_field wouldn’t be included in the output at all.
What if I want to retain the field name EmailID and at the same time use the omitempty
flag? Here’s the modified code:
1
package
main
2
3
import
(
4
"encoding/json"
5
"fmt"
6
)
7
8
type
User
struct
{
9
UserName
string
`json:"first_name,omitempty"`
10
EmailID
string
`json:",omitempty"`
11
Password
string
`json:"-"`
12
}
13
14
func
main
()
{
15
u
:=
User
{
UserName
:
""
,
EmailID
:
""
,
Password
:
"password"
}
16
m
,
_
:=
json
.
Marshal
(
u
)
17
fmt
.
Println
(
string
(
m
))
18
}
The output is:
{}
Let’s look at another example:
1
package
main
2
3
import
(
4
"encoding/json"
5
"fmt"
6
"os"
7
)
8
9
type
Response1
struct
{
10
Page
int
11
Conf
[]
string
12
}
13
14
type
Response2
struct
{
15
Page
int
`json:"page"`
// Field appears in JSON as key "page"
16
Conf
[]
string
`json:"conf"`
// Field appears in JSON as key "conf"
17
}
18
19
func
main
()
{
20
// encoding basic data types to JSON strings
21
bolB
,
_
:=
json
.
Marshal
(
false
)
22
fmt
.
Println
(
string
(
bolB
))
23
24
intB
,
_
:=
json
.
Marshal
(
3
)
25
fmt
.
Println
(
string
(
intB
))
26
27
fltB
,
_
:=
json
.
Marshal
(
87.23
)
28
fmt
.
Println
(
string
(
fltB
))
29
30
strB
,
_
:=
json
.
Marshal
(
"go class"
)
31
fmt
.
Println
(
string
(
strB
))
32
33
// encoding a slice
34
slcD
:=
[]
string
{
"gophercon"
,
"gopherconindia"
,
"go china"
}
35
slcB
,
_
:=
json
.
Marshal
(
slcD
)
36
fmt
.
Println
(
string
(
slcB
))
37
38
// encoding a map
39
// the map's key type must be a string
40
mapD
:=
map
[
string
]
int
{
"pune"
:
5
,
"franklin"
:
7
}
41
mapB
,
_
:=
json
.
Marshal
(
mapD
)
42
fmt
.
Println
(
string
(
mapB
))
43
44
// The JSON package can automatically encode your
45
// custom data types. It will only include exported
46
// fields in the encoded output and will by default
47
// use those names as the JSON keys.
48
res1D
:=
&
Response1
{
49
Page
:
1
,
50
Conf
:
[]
string
{
"gophercon"
,
"gopherconindia"
,
"go china"
}}
51
res1B
,
_
:=
json
.
Marshal
(
res1D
)
52
fmt
.
Println
(
string
(
res1B
))
53
54
// You can use tags on struct field declarations to
55
// customize the encoded JSON key names. Check the
56
// definition of Response2 above to see an example of
57
// such tags.
58
res2D
:=
&
Response2
{
59
Page
:
1
,
60
Conf
:
[]
string
{
"gophercon"
,
"gopherconindia"
,
"go china"
}}
61
res2B
,
_
:=
json
.
Marshal
(
res2D
)
62
fmt
.
Println
(
string
(
res2B
))
63
}
The output is:
false
3
87.23
"go class"
[
"gophercon"
,
"gopherconindia"
,
"go china"
]
{
"franklin"
:
7
,
"pune"
:
5
}
{
"Page"
:
1
,
"Conf"
:
[
"gophercon"
,
"gopherconindia"
,
"go china"
]}
{
"page"
:
1
,
"conf"
:
[
"gophercon"
,
"gopherconindia"
,
"go china"
]}
18.2.3 Decoding
To decode JSON data we use the Unmarshal
function.
func Unmarshal(data []byte, v interface{}) error
We must first create a place where the decoded data will be stored.
var m Message
and call json.Unmarshal
, passing it a []byte of JSON data and a pointer to m.
err := json.Unmarshal(b, &m)
If b contains valid JSON that fits in m, after the call err
will be nil
and the data from b will have been stored in the struct m, as if by an assignment like:
1
m
=
Message
{
2
Name
:
"Alice"
,
3
Body
:
"Hello"
,
4
Time
:
1294706395881547000
,
5
}
How does Unmarshal
identify the fields in which to store the decoded data? For a given JSON key “Foo”, Unmarshal will look through the destination struct’s fields to find (in order of preference):
- An exported field with a tag of “Foo” (see the Go spec for more on struct tags),
- An exported field named “Foo”, or
- An exported field named “FOO” or “FoO” or some other case-insensitive match of “Foo”.
What happens when the structure of the JSON data doesn’t exactly match the Go type?
1
b
:=
[]
byte
(
`{"Name":"Bob","Food":"Pickle"}`
)
2
var
m
Message
3
err
:=
json
.
Unmarshal
(
b
,
&
m
)
Unmarshal
will decode only the fields that it can find in the destination type. In this case, only the Name field of m will be populated, and the Food field will be ignored. This behavior is particularly useful when you wish to pick only a few specific fields out of a large JSON blob. It also means that any unexported fields in the destination struct will be unaffected by Unmarshal.
Unmarshal stores in the interface value: map[string]interface{}
, for JSON objects.
Here’s an example:
1
package
main
2
3
import
(
4
"encoding/json"
5
"fmt"
6
)
7
8
type
Response2
struct
{
9
Page
int
`json:"page"`
10
Conf
[]
string
`json:"conf"`
11
}
12
13
func
main
()
{
14
// We can also decode JSON into custom data types.
15
// This has the advantages of adding additional
16
// type-safety to our programs and eliminating the
17
// need for type assertions when accessing the decoded data.
18
// `Unmarshal` will decode only the fields that it can find
19
// in the destination type. In this case, only the Page and
20
// Conf field of res will be populated, and the Food field
21
// will be ignored.
22
str
:=
`{"page": 1, "conf": ["gophercon", "gopherconindia"], "Food":"Pickle"}`
23
res
:=
&
Response2
{}
24
json
.
Unmarshal
([]
byte
(
str
),
&
res
)
25
fmt
.
Println
(
res
)
26
fmt
.
Println
(
res
.
Conf
[
0
])
27
}
The output is:
&
{
1
[
gophercon
gopherconindia
]}
gophercon
18.2.4 Streaming Encoders and Decoders
The json
package provides Decoder
and Encoder
types to support the common operation of reading and writing streams of JSON data. The NewDecoder
and NewEncoder
functions wrap the io.Reader
and io.Writer
interface types.
func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder
func (enc *Encoder) Encode(v interface{}) error
Encode1 writes the JSON encoding of v to the stream, followed by a newline character.
func (dec *Decoder) Decode(v interface{}) error
Decode2 reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
Previously, to encode JSON data we had used the Marshal
function.
1
package
main
2
3
import
(
4
"encoding/json"
5
"fmt"
6
)
7
8
type
User
struct
{
9
UserName
string
10
EmailID
string
11
Password
string
12
}
13
14
func
main
()
{
15
u
:=
User
{
UserName
:
"IndianGuru"
,
EmailID
:
"satishtalim@gmail.com"
,
Password
:
"password"
}
16
m
,
_
:=
json
.
Marshal
(
u
)
17
fmt
.
Println
(
string
(
m
))
18
}
The output was:
{
"UserName"
:
"IndianGuru"
,
"EmailID"
:
"satishtalim@gmail.com"
,
"Password"
:
"password"
}
Let’s rewrite the above program using json.NewEncoder
as follows:
1
package
main
2
3
import
(
4
"encoding/json"
5
"log"
6
"os"
7
)
8
9
type
User
struct
{
10
UserName
string
11
EmailID
string
12
Password
string
13
}
14
15
func
main
()
{
16
u
:=
User
{
UserName
:
"IndianGuru"
,
EmailID
:
"satishtalim@gmail.com"
,
Password
:
"password"
}
17
18
// Replace the following lines of json1.go
19
// m, _ := json.Marshal(u)
20
// fmt.Println(string(m))
21
// with:
22
enc
:=
json
.
NewEncoder
(
os
.
Stdout
)
23
if
err
:=
enc
.
Encode
(
u
);
err
!=
nil
{
24
log
.
Println
(
err
)
25
}
26
}
The output is the same as before:
{
"UserName"
:
"IndianGuru"
,
"EmailID"
:
"satishtalim@gmail.com"
,
"Password"
:
"password"
}
These Encoder
and Decoder
types can be used in a broad range of scenarios, such as reading and writing to HTTP connections, WebSockets, or files.
18.3 A Fun, Weather Forecast Go Web App
We shall build a fun Go app that displays the current weather forecast for a given city.
18.3.1 Register for an account at Forecast for Developers
Go to https://developer.forecast.io/register3 and register for an account. We shall use their free plan that allows us to use 1000 calls to their API per day. As such, there is no need to enter your credit card details. Also, this page gives you your very own API key that you will use in your program.
18.3.1.1 Study the API documentation
We need to read thro’ the API documentation4 of the Forecast for Developers.
Very briefly:
The Forecast Call
https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE?units=ca
APIKEY
should be your API key as mentioned above. LATITUDE
and LONGITUDE
should be the geographic coordinates of a location in decimal degrees.
The response will be a JSON-formatted object with the following properties defined:
-
latitude
: The requested latitude. -
longitude
: The requested longitude. -
timezone
: The timezone name for the requested location (e.g. America/New_York). -
offset
: The current timezone offset in hours from GMT. -
currently
: A data point (see below) containing the current weather conditions at the requested location.
The following JSON Schema of the API is very informative - forecast.json5
Data Points
A data point object represents the various weather phenomena occurring at a specific instant of time, and has many varied properties. All of these properties (except time
) are optional, and will only be set if we have that type of information for that location and time.
The following JSON Schema of the API is very informative - datapoint.json6
We shall use the two schema (datapoint.json
and forecast.json
) in our program.
Let us define our structs DataPoint
and Forecast
based on these two schema as follows:
1
type
DataPoint
struct
{
2
Time
float64
3
Summary
string
4
Icon
string
5
SunriseTime
float64
6
SunsetTime
float64
7
PrecipIntensity
float64
8
PrecipIntensityMax
float64
9
PrecipIntensityMaxTime
float64
10
PrecipProbability
float64
11
PrecipType
string
12
PrecipAccumulation
float64
13
Temperature
float64
14
TemperatureMin
float64
15
TemperatureMinTime
float64
16
TemperatureMax
float64
17
TemperatureMaxTime
float64
18
DewPoint
float64
19
WindSpeed
float64
20
WindBearing
float64
21
CloudCover
float64
22
Humidity
float64
23
Pressure
float64
24
Visibility
float64
25
Ozone
float64
26
}
27
28
type
Forecast
struct
{
29
Latitude
float64
30
Longitude
float64
31
Timezone
string
32
Offset
float64
33
Currently
DataPoint
34
Junk
string
35
}
Understanding url query string
A query string is a part of an URL that contains data that can be passed to web applications. This data needs to be encoded, and this encoding is done using url.QueryEscape
. It performs what is also commonly called URL encoding7.
1
// addr is a string which contains our address
2
addr
:=
"Pune,India"
3
4
// QueryEscape escapes the addr string so
5
// it can be safely placed inside a URL query
6
safeAddr
:=
url
.
QueryEscape
(
addr
)
7
fmt
.
Println
(
safeAddr
)
// Pune%2CIndia
With url.QueryEscape
our address “Pune,India” becomes “Pune%2CIndia” where %2C is the ASCII keycode in hexadecimal for a comma (,).
The Google Geocoding API
We shall use The Google Geocoding API8 to help us convert addresses (like “1600 Amphitheatre Parkway, Mountain View, CA”) into geographic coordinates (like latitude 37.423021 and longitude -122.083739).
To access the Geocoding API over HTTP, use:
http://maps.googleapis.com/maps/api/geocode/output?parameters
where output
may be either of the following values:
- json (recommended) indicates output in JavaScript Object Notation (JSON)
- xml indicates output as XML
Some parameters are required while some are optional. As is standard in URLs, parameters are separated using the ampersand (&) character.
Required parameters in a geocoding request:
address
— The street address that you want to geocode, in the format used by the national postal service of the country concerned. Additional address elements such as business names and unit, suite or floor numbers should be avoided.
We are not using any optional parameters in our geocoding request.
1
// Geocoding API
2
fullUrl
:=
fmt
.
Sprintf
(
"http://maps.googleapis.com/maps/api/geocode/json?address=%s"
,
safe
\
3
Addr
)
4
fmt
.
Println
(
fullUrl
)
The output is:
http:
//maps.googleapis.com/maps/api/geocode/json?address=Pune%2C+India
In your browser, open the site http://maps.googleapis.com/maps/api/geocode/json?address=Pune%2C+India
the browser output is a huge blob of JSON. This may be difficult to look at in the browser, unless you have the JSONView plugin installed. These extensions are available for Firefox9 and Chrome10. With the extension installed you should be able to see a better view of the JSON returned.
Build the http request
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
NewRequest11 returns a new Request
given a method, URL, and an optional body.
1
// Build the http request
2
req
,
err1
:=
http
.
NewRequest
(
"GET"
,
fullUrl
,
nil
)
3
check
(
err1
,
"NewRequest:"
)
Also, we write a check
function that checks for errors, if any:
1
func
check
(
e
error
,
str
string
)
{
2
if
e
!=
nil
{
3
log
.
Fatal
(
str
,
" "
,
e
)
4
return
5
}
6
}
Create a Client
For control over HTTP client headers, redirect policy, and other settings, create a Client12. A Client
is an HTTP client:
client := &http.Client{}
Send the request via a client
func (c *Client) Do(req *Request) (resp *Response, err error)
Do
sends an HTTP request and returns an HTTP response.
1
resp
,
err2
:=
client
.
Do
(
req
)
2
check
(
err2
,
"Do:"
)
3
4
// Callers should close resp.Body
5
// when done reading from it
6
// Defer the closing of the body
7
defer
resp
.
Body
.
Close
()
Create a Response struct
Observe the JSON output when you open the site http://maps.googleapis.com/maps/api/geocode/json?address=Pune%2C+India
in your browser. There is a results
array, within which there is geometry
, then location
which finally contains lat
and lng
.
Therefore let’s define our struct as follows:
1
type
Response
struct
{
2
Results
[]
struct
{
3
Geometry
struct
{
4
Location
struct
{
5
Lat
float64
6
Lng
float64
7
}
8
}
9
}
10
}
Use a streaming Decoder
1
// Use json.Decode for reading streams of JSON data
2
if
err
:=
json
.
NewDecoder
(
resp
.
Body
).
Decode
(
&
res
);
err
!=
nil
{
3
log
.
Println
(
err
)
4
}
Extract the latitude and logitude
1
// lat, lng as float64
2
lat
:=
res
.
Results
[
0
].
Geometry
.
Location
.
Lat
3
lng
:=
res
.
Results
[
0
].
Geometry
.
Location
.
Lng
Use the Forecast API
Declare a global constant APIKey
as:
const APIKey string = "yourapikey"
safeLatLng := url.QueryEscape(fmt.Sprintf("%.13f,%.13f", lat, lng))
url := fmt.Sprintf("https://api.forecast.io/forecast/%s/%s?units=ca", APIKey, safeLatLng)
- Remember to replace
APIKey
above with your actual api key. -
%.13f
is used to convertfloat64
to astring
-
?units=ca
- the API request was optionally modified through the use of query parameterunits=ca
will return temperatures in degrees Celsius.
Use http.Get
func Get(url string) (resp *Response, err error)
Get14 issues a GET
to the specified URL.
1
resp
,
err
:=
http
.
Get
(
url
)
2
check
(
err
,
"Get:"
)
3
defer
resp
.
Body
.
Close
()
Use the Forecast struct to get the results
1
var
f
Forecast
2
if
err
:=
json
.
NewDecoder
(
resp
.
Body
).
Decode
(
&
f
);
err
!=
nil
{
3
log
.
Println
(
err
)
4
}
5
6
fmt
.
Println
(
"The Weather at "
,
addr
)
7
fmt
.
Println
(
"Timezone = "
,
f
.
Timezone
)
8
fmt
.
Println
(
"Temp in Celsius = "
,
f
.
Currently
.
Temperature
)
9
fmt
.
Println
(
"Summary = "
,
f
.
Currently
.
Summary
)
Here’s the full program:
1
package
main
2
3
import
(
4
"encoding/json"
5
"fmt"
6
"log"
7
"net/http"
8
"net/url"
9
)
10
11
type
DataPoint
struct
{
12
Time
float64
13
Summary
string
14
Icon
string
15
SunriseTime
float64
16
SunsetTime
float64
17
PrecipIntensity
float64
18
PrecipIntensityMax
float64
19
PrecipIntensityMaxTime
float64
20
PrecipProbability
float64
21
PrecipType
string
22
PrecipAccumulation
float64
23
Temperature
float64
24
TemperatureMin
float64
25
TemperatureMinTime
float64
26
TemperatureMax
float64
27
TemperatureMaxTime
float64
28
DewPoint
float64
29
WindSpeed
float64
30
WindBearing
float64
31
CloudCover
float64
32
Humidity
float64
33
Pressure
float64
34
Visibility
float64
35
Ozone
float64
36
}
37
38
type
Forecast
struct
{
39
Latitude
float64
40
Longitude
float64
41
Timezone
string
42
Offset
float64
43
Currently
DataPoint
44
Junk
string
45
}
46
47
type
Response
struct
{
48
Results
[]
struct
{
49
Geometry
struct
{
50
Location
struct
{
51
Lat
float64
52
Lng
float64
53
}
54
}
55
}
56
}
57
58
// Replace the text `yourapikey` below with your actual api key
59
const
APIKey
string
=
"yourapikey"
60
61
func
main
()
{
62
Get
()
63
}
64
65
func
check
(
e
error
,
str
string
)
{
66
if
e
!=
nil
{
67
log
.
Fatal
(
str
,
" "
,
e
)
68
return
69
}
70
}
71
72
func
Get
()
{
73
var
addr
string
74
75
fmt
.
Println
(
"Enter City eg. Pune, India: "
)
76
fmt
.
Scanf
(
"%s"
,
&
addr
)
77
78
// QueryEscape escapes the addr string so
79
// it can be safely placed inside a URL query
80
safeAddr
:=
url
.
QueryEscape
(
addr
)
81
82
// Geocoding API
83
fullUrl
:=
fmt
.
Sprintf
(
"http://maps.googleapis.com/maps/api/geocode/json?address=%s"
,
saf
\
84
eAddr
)
85
86
// Build the http request
87
req
,
err1
:=
http
.
NewRequest
(
"GET"
,
fullUrl
,
nil
)
88
check
(
err1
,
"NewRequest:"
)
89
90
// For control over HTTP client headers,
91
// redirect policy, and other settings,
92
// create a Client
93
// A Client is an HTTP client
94
client
:=
&
http
.
Client
{}
95
96
// Send the request via a client
97
// Do sends an HTTP request and
98
// returns an HTTP response
99
resp
,
err2
:=
client
.
Do
(
req
)
100
check
(
err2
,
"Do:"
)
101
102
// Callers should close resp.Body
103
// when done reading from it
104
// Defer the closing of the body
105
defer
resp
.
Body
.
Close
()
106
107
var
res
Response
108
109
// Use json.Decode or json.Encode for reading or writing streams of JSON data
110
if
err
:=
json
.
NewDecoder
(
resp
.
Body
).
Decode
(
&
res
);
err
!=
nil
{
111
log
.
Println
(
err
)
112
}
113
114
// lat, lng as float64
115
lat
:=
res
.
Results
[
0
].
Geometry
.
Location
.
Lat
116
lng
:=
res
.
Results
[
0
].
Geometry
.
Location
.
Lng
117
118
safeLatLng
:=
url
.
QueryEscape
(
fmt
.
Sprintf
(
"%.13f,%.13f"
,
lat
,
lng
))
119
url
:=
fmt
.
Sprintf
(
"https://api.forecast.io/forecast/%s/%s?units=ca"
,
APIKey
,
safeLatLng
)
120
121
resp
,
err
:=
http
.
Get
(
url
)
122
check
(
err
,
"Get:"
)
123
defer
resp
.
Body
.
Close
()
124
125
var
f
Forecast
126
127
if
err
:=
json
.
NewDecoder
(
resp
.
Body
).
Decode
(
&
f
);
err
!=
nil
{
128
log
.
Println
(
err
)
129
}
130
131
fmt
.
Println
(
"The Weather at "
,
addr
)
132
fmt
.
Println
(
"Timezone = "
,
f
.
Timezone
)
133
fmt
.
Println
(
"Temp in Celsius = "
,
f
.
Currently
.
Temperature
)
134
fmt
.
Println
(
"Summary = "
,
f
.
Currently
.
Summary
)
135
}
The output is:
Enter
City
eg
.
Pune
,
India
:
Pune
,
India
The
Weather
at
Pune
,
Timezone
=
Asia
/
Kolkata
Temp
in
Celsius
=
30.59
Summary
=
Mostly
Cloudy
That’s it!
- http://golang.org/pkg/encoding/json/#Encoder.Encode↩
- http://golang.org/pkg/encoding/json/#Decoder.Decode↩
- https://developer.forecast.io/register↩
- https://developer.forecast.io/docs/v2↩
- https://github.com/kingsfleet/rest-metadata/blob/master/forecast.io/forecast.json↩
- https://github.com/kingsfleet/rest-metadata/blob/master/forecast.io/datapoint.json↩
- http://en.wikipedia.org/wiki/Query_string#URL_encoding↩
- https://developers.google.com/maps/documentation/geocoding/↩
- https://addons.mozilla.org/en-us/firefox/addon/jsonview/↩
- https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc↩
- http://golang.org/pkg/net/http/#NewRequest↩
- http://golang.org/pkg/net/http/#Client↩
- http://mholt.github.io/json-to-go/↩
- http://golang.org/pkg/net/http/#Get↩
19. TCP programming using Go
19.1 TCP Sockets
If you are a client you need an API that will allow you to connect to a service and then to send messages to that service and read replies back from the service.
If you are a server, you need to be able to bind to a port and listen at it. When a message comes in you need to be able to read it and write back to the client.
19.2 Package net
Usage: import "net"
Package net
provides a portable interface for network I/O, including TCP/IP and UDP.
Type net.Conn1 interface is a generic stream-oriented network connection. Multiple goroutines may invoke methods on a net.Conn
simultaneously.
Type net.TCPConn2 is an implementation of the net.Conn
interface for TCP network connections. net.TCPConn
has two major methods of interest:
func (c *TCPConn) Write(b []byte) (n int, err error)
func (c *TCPConn) Read(b []byte) (n int, err error)
A net.TCPConn
is used by both a client and a server to read and write messages.
19.2.1 TCP Client
Once a client has established a TCP address for a service, it “dials” the service. If succesful, the dial returns a net.TCPConn
for communication. The client and the server exchange messages on this. Typically a client writes a request to the server using the net.TCPConn
, and reads a response from the net.TCPConn
. This continues until either (or both) sides close the connection. A TCP connection is established by the client using the function:
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error)
net.DialTCP
connects to the remote address raddr
on the network net
, which must be “tcp”, “tcp4” (IPv4-only i.e. Internet Protocol version 4), or “tcp6” (IPv6-only i.e. Internet Protocol version 6). If laddr
is not nil, it is used as the local address for the connection.
Note: Use “tcp” or “tcp4” or “tcp6” depending upon your computer.
A simple example can be provided by a client to a web (HTTP) server.
One of the possible messages that a client can send is the “GET” message. This queries a server for information about the server and a document on that server. The server’s response contains header as well as body, and the connection closes after the response. The request sent to query an HTTP server could be:
GET / HTTP/1.0\r\n\r\n
1
package
main
2
3
import
(
4
"fmt"
5
"io/ioutil"
6
"log"
7
"net"
8
"os"
9
)
10
11
func
check
(
e
error
,
str
string
)
{
12
if
e
!=
nil
{
13
log
.
Fatal
(
str
,
" "
,
e
)
14
return
15
}
16
}
17
18
func
main
()
{
19
if
len
(
os
.
Args
)
!=
2
{
20
fmt
.
Fprintf
(
os
.
Stderr
,
"Usage: %s host:port "
,
os
.
Args
[
0
])
21
os
.
Exit
(
1
)
22
}
23
service
:=
os
.
Args
[
1
]
24
tcpAddr
,
err
:=
net
.
ResolveTCPAddr
(
"tcp"
,
service
)
25
check
(
err
,
"ResolveTCPAddr:"
)
26
27
conn
,
err
:=
net
.
DialTCP
(
"tcp"
,
nil
,
tcpAddr
)
28
check
(
err
,
"DialTCP:"
)
29
30
// response contains header as well as body, and the connection closes after the response.
31
_
,
err
=
conn
.
Write
([]
byte
(
"GET / HTTP/1.0\r\n\r\n"
))
32
check
(
err
,
"Write:"
)
33
34
result
,
err
:=
ioutil
.
ReadAll
(
conn
)
35
check
(
err
,
"ReadAll:"
)
36
37
fmt
.
Println
(
string
(
result
))
38
}
To run this program, type:
go run get_info.go www.golang.com:80
The output is:
HTTP/1.0 302 Found
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Content-Type: text/html; charset=UTF-8
Location: http://www.google.co.in/?gfe_rd=cr&
ei=CkNdVf-iMYT5vQS_w4H4CQ
Content-Length: 261
Date: Thu, 21 May 2015 02:29:30 GMT
Server: GFE/2.0
Alternate-Protocol: 80:quic,p=0
Pragma: no-cache
Expires: Tue, 01 Jan 1971 02:00:00 GMT
<HTML><HEAD><meta
http-equiv=
"content-type"
content=
"text/html;charset=utf-8"
>
<TITLE>
302 Moved</TITLE></HEAD><BODY>
<H1>
302 Moved</H1>
The document has moved
<A
HREF=
"http://www.google.co.in/?gfe_rd=cr&ei=CkNdVf-iMYT5vQS_w4H4CQ"
>
here</A>
.
</BODY></HTML>
In this program:
- We use the host:port as www.golang.com:80
-
net.ResolveTCPAddr3 parses
www.golang.com:80
as a TCP address of the form “host:port” and resolves a pair of domain name and port name on the network net, which must be “tcp”, “tcp4” or “tcp6”. -
net.DialTCP4 connects to the remote address
www.golang.com:80
(as a TCP address) on the networktcp
(which we have used), which must be “tcp”, “tcp4”, or “tcp6”. This way we have now established a connection with the remote host. - conn.Write5 sends the request string.
- Next we read and print the response. We read essentially a single response from the server. This will be terminated by end-of-file on the connection. However, it may consist of several TCP packets, so we need to keep reading till the end of file. The
io/ioutil
functionReadAll
will look after these issues and return the complete response.
19.2.2 A Daytime server
A server registers itself on a port, and listens on that port. Then it blocks on an “accept” operation, waiting for clients to connect. When a client connects, the accept call returns, with a connection object. The daytime service is very simple and just writes the current time to the client, closes the connection, and resumes waiting for the next client.
1
package
main
2
3
import
(
4
"fmt"
5
"log"
6
"net"
7
"time"
8
)
9
10
func
check
(
e
error
,
str
string
)
{
11
if
e
!=
nil
{
12
log
.
Fatal
(
str
,
" "
,
e
)
13
return
14
}
15
}
16
17
func
main
()
{
18
listener
,
err
:=
net
.
Listen
(
"tcp"
,
":6000"
)
19
check
(
err
,
"Listen:"
)
20
21
for
{
22
conn
,
err
:=
listener
.
Accept
()
23
if
err
!=
nil
{
24
log
.
Println
(
err
)
25
conn
.
Close
()
26
break
27
}
28
fmt
.
Println
(
"Connected to client..."
)
29
30
daytime
:=
fmt
.
Sprintf
(
"The Date Time is: %s"
,
time
.
Now
())
31
32
conn
.
Write
([]
byte
(
daytime
))
// don't care about return value
33
conn
.
Close
()
// we're finished with this client
34
fmt
.
Println
(
"Client connection closed."
)
35
}
36
}
- net.Listen6 listens on port 6000.
-
Accept7 implements the
Accept
method in theListener
interface; it waits for the next call and returns a genericConn
. - When a client connects to it, it will respond by sending the daytime string to it and then return to waiting for the next client.
- The server should run forever, so that if any error occurs with a client, the server just ignores that client and carries on. A client could otherwise try to mess up the connection with the server, and bring it down.
Run the telnet client as:
telnet localhost 6000
you should see the date-time at the server location.
20. Unit testing and Benchmarking Go programs
20.1 Testing
Usage: import "testing"
Package testing1 provides support for automated testing of Go packages. It is intended to be used in concert with the go test
command.
Let us create a folder mult
as follows:
$
mkdir $GOPATH
/src/github.com/SatishTalim/mult
Replace SatishTalim
with your GitHub id.
Next, create a file named mult.go
inside that folder, containing the following Go code:
1
package
mult
2
3
func
Mult2Ints
(
i
,
j
int
)
int
{
4
return
i
*
j
5
}
To write a new test suite, create a file whose name ends _test.go
(mult_test.go
in our case) that contains the TestXxx
functions as shown below. Put the file in the same package as the one being tested. The file will be excluded from regular package builds but will be included when the go test
command is run.
1
//same package name as source file
2
package
mult
3
4
//import go package for testing related functionality
5
import
"testing"
6
7
//test function starts with "Test" and takes a pointer to type testing.T
8
func
TestMult2Ints_1
(
t
*
testing
.
T
)
{
9
//try a unit test on function
10
if
Mult2Ints
(
3
,
4
)
!=
12
{
11
// log error if it did not work as expected
12
t
.
Error
(
"Mult2Ints did not work as expected."
)
13
}
else
{
14
// log some info if you want
15
t
.
Log
(
"one test passed."
)
16
}
17
}
18
19
//test function starts with "Test" and takes a pointer to type testing.T
20
func
TestMult2Ints_2
(
t
*
testing
.
T
)
{
21
//Indicate that this test failed and log the string as info
22
t
.
Error
(
"this is just hardcoded as an error."
)
23
}
Please note:
- the file name has to end with
_test.go
to be picked up as a set of tests bygo test
- the package name has to be the same as in the source file that has to be tested
- you have to
import
the packagetesting
- all test functions should start with
Test
to be run as a test - the tests will be executed in the same order that they are appear in the source
- the test function
TestXxx
take a pointer to the typetesting.T
. You use it to record the test status and also for logging - the signature of the test function should always be
func TestXxx ( *testing.T)
. You can have any combination of alphanumeric characters for the Xxx part, the only constraint that it should not begin with a small alphabet, [a-z] - a call to any of the following functions of
testing.T
within the test codeError
,Errorf
,FailNow
,Fatal
,FatalIf
will indicate to go test that the test has failed
Next in the folder $GOPATH/src/github.com/SatishTalim/mult
, run your tests by executing go test
at the command line:
$
go test
--- FAIL: Test_Mult2Ints_2 (
0.00s)
mult_test.go:16: this is just hardcoded as an error.
FAIL
exit
status 1
FAIL github.com/SatishTalim/mult 0.103s
What happened to the first test? By default go test
hides all passed results. If you want to see all the results, including the passed tests, execute it with the -v
option:
$
go test
-v
===
RUN Test_Mult2Ints_1
--- PASS: Test_Mult2Ints_1 (
0.00s)
mult_test.go:11: one test
passed.
===
RUN Test_Mult2Ints_2
--- FAIL: Test_Mult2Ints_2 (
0.00s)
mult_test.go:16: this is just hardcoded as an error.
FAIL
exit
status 1
FAIL github.com/SatishTalim/mult 0.073s
20.2 Benchmarks
Benchmark tests allow you to check the performance of your code. It is useful when you want to test the performance between different solutions to the same problem and see which solution performs better.
Please note:
- benchmark tests must have the signature
func BenchmarkXxx ( *testing.B)
. You can have any combination of alphanumeric characters for the Xxx part, the only constraint it should not begin with a small alphabet, [a-z] -
go test
does not run the benchmark tests by default - benchmark tests are executed by the
go test
command when its-bench
flag is provided -
go test
automatically figures out how long the test has to be run to get a reasonable benchmark result - to make that work, use the field
testing.B.N
within a loop to repeatedly run a certain function - no benchmark results if all the unit tests do not pass successfully. So either make sure all tests pass successfully, which of course should be the plan in any case
Add a new file shown below:
1
//same package name as source file
2
package
mult
3
4
//import go package for testing related functionality
5
import
"testing"
6
7
//benchmark function starts with "Benchmark" and takes a pointer to type testing.B
8
func
BenchmarkTheMultIntsFunction
(
b
*
testing
.
B
)
{
9
//use b.N for looping
10
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
11
Mult2Ints
(
4
,
5
)
12
}
13
}
14
15
//benchmark function starts with "Benchmark" and takes a pointer to type testing.B
16
func
BenchmarkTimeConsumingFunction
(
b
*
testing
.
B
)
{
17
//stop the performance timer temporarily while doing initialization
18
b
.
StopTimer
()
19
20
//do any time consuming initialization functions here ...
21
//database connection, reading files, network connection, etc.
22
23
//restart timer
24
b
.
StartTimer
()
25
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
26
Mult2Ints
(
4
,
5
)
27
}
28
}
Run the program as follows:
$
go test
-v -run=
"none"
-bench .
PASS
Benchmark_TheMultIntsFunction 2000000000 0.65 ns/op
Benchmark_TimeConsumingFunction 2000000000 0.65 ns/op
ok github.com/SatishTalim/mult 2.974s
In our call to , we specified the option passing go test -run
the string “none” to make sure no unit tests are run prior to running the specified benchmark functions. Both these options take a regular expression to filter the tests to run. Since there is no unit test function that has none
in its name, it eliminates any unit tests from running.
The benchmark function must run the target code b.N times. During benchark execution, b.N is adjusted until the benchmark function lasts long enough to be timed reliably.
The output:
Benchmark_TheMultIntsFunction 2000000000 0.65 ns/op
means that the loop ran 2000000000 times at a speed of 0.65 ns per loop.
The default minimum run time for a benchmark is 1 second. You can see how the framework still ran the test for approximately three seconds.
Let’s look at a set of benchmark functions that reveal the fastest way to convert a float value to a string. In the standard library there are two different ways to convert a float value to a string.
Let us create a folder conv
as follows:
$
mkdir $GOPATH
/src/github.com/SatishTalim/conv
Replace SatishTalim
with your GitHub id.
Next, create a file named conv_test.go
inside that folder, containing the following Go code:
1
package
conv
2
3
import
(
4
"fmt"
5
"testing"
6
)
7
8
// Sample benchmarks to test which function is better for converting
9
// an float into a string. First using the fmt.Sprintf function.
10
func
BenchmarkSprintf
(
b
*
testing
.
B
)
{
11
no
:=
34.56
12
13
b
.
ResetTimer
()
14
15
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
16
fmt
.
Sprintf
(
"%f"
,
no
)
17
}
18
}
Run the program as follows:
$
go test
-v -run=
"none"
-bench=
"BenchmarkSprintf"
testing: warning: no tests to run
PASS
BenchmarkSprintf 1000000 1365 ns/op
ok github.com/SatishTalim/conv 3.316s
The performance of using the Sprintf
function in this context is it takes 1365 nanoseconds on average per call.
The final output from running the benchmark shows ok to represent the benchmark finished properly. Then the name of the code file that was executed is displayed and finally the total time the benchmark ran. The default minimum run time for a benchmark is 1 second. You can see how the framework still ran the test for approximately more than three seconds.
Now, let us write another benchmark function in our source file conv_test.go
.
1
package
conv
2
3
import
(
4
"fmt"
5
"strconv"
6
"testing"
7
)
8
9
// Sample benchmarks to test which function is better for converting
10
// an float into a string. First using the fmt.Sprintf function.
11
func
BenchmarkSprintf
(
b
*
testing
.
B
)
{
12
no
:=
34.56
13
14
b
.
ResetTimer
()
15
16
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
17
fmt
.
Sprintf
(
"%f"
,
no
)
18
}
19
}
20
21
// BenchmarkFormat provides performance numbers for the
22
// strconv.FormatInt function.
23
func
BenchmarkFormat
(
b
*
testing
.
B
)
{
24
no
:=
34.56
25
26
b
.
ResetTimer
()
27
28
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
29
strconv
.
FormatFloat
(
no
,
'f'
,
2
,
64
)
30
}
31
}
Run the program as follows:
$
go test
-v -run=
"none"
-bench=
"BenchmarkFormat"
testing: warning: no tests to run
PASS
BenchmarkFormat 1000000 1165 ns/op
ok github.com/SatishTalim/conv 2.235s
The BenchmarkFormat
function above, benchmarks the use of the function FormatFloat
from the strconv
package.
The call to b.ResetTimer
, which is used in the two benchmark functions. This method is useful to reset the timer when initialization is required before the code can start executing the loop. To have the most accurate benchmark times you can, use this method.
The results show that the test function runs BenchmarkFormat
the fastest at 1165 nanoseconds per operation.
- http://golang.org/pkg/testing/↩
21. Concurrency
Rob Pike - Concurrency is not parallelism1:
When people hear the word concurrency they often think of parallelism, a related but quite distinct concept. In programming, concurrency is the composition of independently executing processes, while parallelism is the simultaneous execution of (possibly related) computations. Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.
Go allows us to write concurrent programs. It provides goroutines and importantly, the ability to communicate between them.
21.1 Goroutines
They’re called goroutines2 because the existing terms—threads, coroutines, processes, and so on—convey inaccurate connotations. A goroutine has a simple model: it is a function executing concurrently with other goroutines in the same address space. It is lightweight, costing little more than the allocation of stack space. And the stacks start small, so they are cheap, and grow by allocating (and freeing) heap storage as required.
Goroutines are multiplexed onto multiple OS threads so if one should block, such as while waiting for I/O, others continue to run. Their design hides many of the complexities of thread creation and management.
Prefix a function or method call with the go
keyword to run the call in a new goroutine. When the call completes, the goroutine exits, silently.
go list.Sort() // run list.Sort concurrently; don't wait for it.
An example:
1
package
main
2
3
import
(
4
"fmt"
5
"math/rand"
6
"time"
7
)
8
9
func
f
(
goRtn
int
)
{
10
for
i
:=
0
;
i
<
3
;
i
++
{
11
fmt
.
Println
(
"Goroutine "
,
goRtn
,
" : Value "
,
i
)
12
amt
:=
time
.
Duration
(
rand
.
Intn
(
250
))
13
time
.
Sleep
(
time
.
Millisecond
*
amt
)
14
}
15
}
16
func
main
()
{
17
for
i
:=
0
;
i
<
3
;
i
++
{
18
go
f
(
i
)
19
}
20
var
input
string
21
fmt
.
Scanln
(
&
input
)
22
}
This program consists of two goroutines
. The first goroutine
is implicit and is the main
function itself. The second goroutine
is created when we call go f(0)
. Normally when we invoke a function our program will execute all the statements in a function and then return to the next line following the invocation. With a goroutine
we return immediately to the next line and don’t wait for the function to complete. This is why the call to the Scanln
function has been included; without it the program would exit before being given the opportunity to print all the numbers.
Goroutines
are lightweight and we can easily create thousands of them. Our program runs 3 goroutines
.
We add some delay to the function using time.Sleep3 and rand.Intn4.
f
prints out the numbers from 0 to 3, waiting between 0 and 250 ms after each one. The goroutines
should now run simultaneously.
The output I get is:
Goroutine
0
:
Value
0
Goroutine
1
:
Value
0
Goroutine
2
:
Value
0
Goroutine
0
:
Value
1
Goroutine
2
:
Value
1
Goroutine
1
:
Value
1
Goroutine
0
:
Value
2
Goroutine
2
:
Value
2
Goroutine
1
:
Value
2
21.2 Channels
Channels provide a way for two goroutines
to communicate with one another and synchronize their execution.
Channels have several characteristics: the type of element you can send through a channel, capacity (or buffer size) and direction of communication specified by a <-
operator. Thus, you can send values into channels from one goroutine
and receive those values into another goroutine
with the channel operator, <-
.
Channels are first-class values and can be used anywhere like other values: as struct elements, function arguments, function returning values and even like a type for another channel.
1
chan
<-
v
// Send v to channel chan.
2
v
:=
<-
chan
// Receive from chan, and
3
// assign value to v.
(The data flows in the direction of the arrow.)
Channels must be created using the built-in function make
before use:
1
ch
:=
make
(
chan
int
)
By default, sends and receives block until the other side is ready. This allows goroutines
to synchronize without explicit locks or condition variables.
Let us look at a simple example:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
//Create a new channel with
7
//make(chan val-type). Channels are
8
//typed by the values they convey
9
messages
:=
make
(
chan
string
)
10
11
//Send a value into a channel using
12
//the channel <- syntax. Here we send
13
//"ping" to the messages channel we
14
//made above, from a new goroutine
15
go
func
()
{
messages
<-
"ping"
}()
16
17
//The <-channel syntax receives a
18
//value from the channel. Here we'll
19
//receive the "ping" message we sent
20
//above and print it out
21
msg
:=
<-
messages
22
fmt
.
Println
(
msg
)
23
}
When we run the program the “ping” message is successfully passed from one goroutine
to another via our channel.
By default sends and receives block until both the sender and receiver are ready. This property allowed us to wait at the end of our program for the “ping” message without having to use any other synchronization.
21.2.1 Channel direction
We can specify a direction on a channel type thus restricting it to either sending or receiving. For example if we have the following function signature:
1
func
pinger
(
c
chan
<-
string
)
Now c can only be sent to. Attempting to receive from c will result in a compiler error. Similarly we can change signature to this:
1
func
printer
(
c
<-
chan
string
)
A channel that doesn’t have these restrictions is known as bi-directional. A bi-directional channel can be passed to a function that takes send-only or receive-only channels, but the reverse is not true.
21.2.2 Unbuffered channel
Let’s understand this with an example:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
fmt
.
Println
(
"From main()"
)
7
8
// Create a channel to synchronize goroutines
9
done
:=
make
(
chan
bool
)
10
11
// Execute fmt.Println in goroutine
12
go
func
()
{
13
sum
:=
0
14
for
i
:=
0
;
i
<
10000
;
i
++
{
15
sum
+=
i
16
}
17
fmt
.
Println
(
"From goroutine - done."
)
18
19
// Tell the main function everything is done.
20
// This channel is visible inside this goroutine because
21
// it is executed in the same address space.
22
done
<-
true
23
}()
24
25
<-
done
// Read operation: Wait for the goroutine to finish
26
}
Observe the output. The line “From main()” is blocked i.e. it is not printed until the goroutine has writen data to the channel. Thus the output you see is:
From
main
()
From
goroutine
-
done
.
both being printed at the same time.
Note: If the channel is unbuffered, the sender blocks until the receiver has received the value.
In the above program, the done
channel has no buffer (as we did not specify its capacity). All operations on unbuffered channels block the execution until both sender and receiver are ready to communicate. That’s why unbuffered channels are also called synchronous. In our case the reading operation <-done
in the main function will block its execution until the goroutine will write data to the channel. Thus the program ends only after the reading operation (<-done
) succeeds.
22. Additional Exercises
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
x
:=
"text"
7
fmt
.
Println
(
x
[
0
])
//print 116
8
}
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
data
:=
[]
int
{
1
,
2
,
3
}
7
i
:=
0
8
++
i
//error
9
fmt
.
Println
(
data
[
i
++
])
//error
10
}
1
package
main
2
3
import
(
4
"fmt"
5
"log"
6
"net/http"
7
"io/ioutil"
8
)
9
10
func
check
(
e
error
,
str
string
)
{
11
if
e
!=
nil
{
12
log
.
Fatal
(
str
,
" "
,
e
)
13
return
14
}
15
}
16
17
func
main
()
{
18
resp
,
err
:=
http
.
Get
(
"https://api.ipify.org?format=json"
)
19
defer
resp
.
Body
.
Close
()
20
check
(
err
,
"http.Get:"
)
21
22
body
,
err
:=
ioutil
.
ReadAll
(
resp
.
Body
)
23
check
(
err
,
"ReadAll:"
)
24
25
fmt
.
Println
(
string
(
body
))
26
}
22.1 Solutions
Exercise A1 Solution1. Another solution for A12.
Exercise A2 Reason: The index operator on a string returns a byte value, not a character (like it’s done in other languages). For more details, read the topic on conversions3.
Exercise A3 Reason: Many languages have increment and decrement operators. Unlike other languages, Go doesn’t support the prefix version of the operations. You also can’t use these two operators in expressions.
Exercise A4 Solution:
Exercise A5 Reason: When you make requests using the standard http
library you get a http
response variable. If you don’t read the response body you still need to close it. You must do it for empty responses too. It’s very easy to forget especially. Some new Go developers do try to close
the response body, but they do it in the wrong place as was done in the given program. The most common why to close
the response body is by using a defer
call after the http
response error
check.
Most of the time when your http
request fails the resp variable will be nil
and the err
variable will be non-nil. However, when you get a redirection failure both variables will be non-nil.
A better way is:
1
resp
,
err
:=
http
.
Get
(
"https://api.ipify.org?format=json"
)
2
defer
func
()
{
if
resp
!=
nil
{
resp
.
Body
.
Close
()}}()
3
check
(
err
,
"http.Get:"
)
Exercise A6 Solution.
23. Additional Reading
23.1 Testable Examples in Go
Godoc examples are snippets of Go code that are displayed as package documentation and that are verified by running them as tests. They can also be run by a user visiting the godoc web page for the package and clicking the associated “Run” button.
Appendix A
Downloading Go
Visit the Go project’s downloads page1 and select the binary distribution that matches your operating system and processor architecture.
Install the Go tools
Read through the installation guide2 for Linux, Mac OS X, FreeBSD and Windows.
The Go binary distributions assume they will be installed in /usr/local/go
(or c:\Go
under Windows), but it is possible to install them in a different location. If you do this, you will need to set the GOROOT
environment variable to that directory when using the Go tools.
For example, if you installed Go to your home directory you should add the following commands to $HOME/.profile
:
export
GOROOT
=
$
HOME
/
go
export
PATH
=
$
PATH
:
$
GOROOT
/
bin
Under Windows, you may set environment variables through the “Environment Variables” button on the “Advanced” tab of the “System” control panel. Some versions of Windows provide this control panel through the “Advanced System Settings” option inside the “System” control panel.
The Go-environment works with a small number of OS environment variables. They are not required for building the Go-environment, but by setting them and thus overriding the defaults the Go compilation environment can be customized. I have set the following:
Name | Value |
---|---|
GOROOT | C:\go |
GOOS | windows |
GOARCH | 386 |
Environment Variables
On Windows:
In the same dialog-window: Edit the PATH-variable as follows:
C:
\
go
\
bin
;
...
rest
of
PATH
...
Test your installation
Check that Go is installed correctly by building a simple program, as follows.
Create a file named hello_world.go
in some folder (for now) and put the following program in it:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
fmt
.
Println
(
"Hello, world."
)
7
}
Then from the folder where you have saved the file hello_world.go
run it with the go
tool:
$
go run hello_world.go
Hello, world.
If you see the “Hello, world.” message then your Go installation is working.
Go Code Organization
The go tool3 is the standard way to fetch, build, and install Go packages and commands. The go
tool requires you to organize your code in a specific way.
Workspaces
Go code must be kept inside a workspace. A workspace is a directory hierarchy with three directories at its root:
-
src
contains Go source files organized into packages (one package per directory), -
pkg
contains package objects, and -
bin
contains executable commands.
The go
tool builds source packages and installs the resulting binaries to the pkg
and bin
directories.
The src
subdirectory typically contains version control repositories (such as for Git) that track the development of one or more source packages.
The GOPATH environment variable
The GOPATH
environment variable specifies the location of your workspace.
To get started, create a workspace directory and set GOPATH
accordingly. Your workspace can be located wherever you like. Note that this must not be the same path as your Go installation.
On my Windows computer, I have set GOPATH=C:\go_projects\go
. Next I update my system environment variable PATH
to include my workspace bin
subdirectory i.e. PATH=%PATH%;%GOPATH%\bin;
We need to do the same on *nix or Mac as well.
mkdir
$
HOME
/
go_projects
/
go
export
GOPATH
=
$
HOME
/
go_projects
/
go
export
PATH
=
$
PATH
:
$
GOPATH
/
bin
C:\go_projects
\---go
+---bin
+---pkg
\---src
Package paths
The packages from the standard library are given short paths such as fmt
and net/http
. For your own packages, you must choose a base path that is unlikely to collide with future additions to the standard library or other external libraries. If you have a GitHub account at github.com/user
, that should be your base path. We will use
github.com/user as our base path. Create a directory inside your workspace in which to keep the source code. I have created the folder C:\go_projects\go\src\github.com\SatishTalim
.
Editing a Go program
Go programs are written as plain text Unicode using the UTF-8 encoding. All of Go’s keywords and operators use ASCII characters; however, Go identifiers can start with any Unicode letter followed by any Unicode letters or digits, so Go programmers can freely use their native language.
A Go program
To compile and run a simple program, first choose a package path and create a corresponding package directory inside your workspace:
$
mkdir $GOPATH
/src/github.com/SatishTalim/hello
Next, create a file named hello.go
inside that directory, containing the following Go code:
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
fmt
.
Printf
(
"Hello, world.\n"
)
7
}
Now you can build and install that program with the go
tool:
$
cd
$GOPATH
/src/github.com/SatishTalim/hello
$
go install
The go
tool will only print output when an error occurs, so if these commands produce no output they have executed successfully.
The above command i.e. go install4 compiles, builds the hello
executable binary and then installs that binary to the workspace’s bin
directory as hello
(or, under Windows, hello.exe
).
You can now run the program by typing:
$
hello
Hello, world.
Appendix B - Project: redditnews for Baby Gophers

I am keen to be abreast with what’s happening in the Golang world. To that end, we will write a command-line program (redditnews.go
) that fetches and displays the latest headlines from the golang page on Reddit.
The program will:
- make an HTTP request to the Reddit API.
- decode the JSON response into a Go data structure, and
- display each link’s author, score, URL and title.
We will then be building a bare-bones News Reader package (redditnews
) that gives us the latest news and headlines from the Golang Sub-Reddit, using Reddit’s API.
Once the project is ready, we will use GMail to send an email which will contain the information fetched by the package redditnews
.
Use GitHub
One of the best places to share your code with friends, co-workers, classmates, and complete strangers is GitHub1.
Create an account
On the main screen that shows up, enter your username
, your email
and create a password
. Next, click on Sign up for GitHub
. On the next screen, The ‘Free’ plan is automatically chosen for you. Just click on the Finish sign up
button. You will receive an email asking you to ‘Confirm your email’. Please do that.
Set up Git
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
If you are new to Git, then learn Git in 15 minutes2.
Next, Set Up Git3 on your computer as per the guide.
Complete Appendix A
I am assuming that you have completed Appendix A.
redditnews.go (First Iteration)
This is the first draft of my program.
In your browser, open the site http://reddit.com/r/golang.json4 the browser output is a huge blob of JSON that we receive from the Golang Subreddit. This may be difficult to look at in the browser, unless you have the JSONView plugin installed. These extensions are available for Firefox5 and Chrome. With the extension installed, here’s a partial view of the JSON:

Now let’s write the first draft of our program.
Make a new folder and cd to it as follows:
$
mkdir $GOPATH
/src/github.com/SatishTalim/redditnews
$
cd
$GOPATH
/src/github.com/SatishTalim/redditnews
In this folder, create a file named redditnews.go
, open it in your favorite editor, and add the following lines:
package
main
import
(
"io"
"log"
"net/http"
"os"
)
func
main
()
{
resp
,
err
:=
http
.
Get
(
"http://reddit.com/r/golang.json"
)
if
err
!=
nil
{
log
.
Fatal
(
err
)
}
defer
resp
.
Body
.
Close
()
if
resp
.
StatusCode
!=
http
.
StatusOK
{
log
.
Fatal
(
resp
.
Status
)
}
_
,
err
=
io
.
Copy
(
os
.
Stdout
,
resp
.
Body
)
if
err
!=
nil
{
log
.
Fatal
(
err
)
}
}
- Like all Go programs that need to be executed, our program has a package
main
. - Package io6 provides basic interfaces to I/O primitives.
- On an error we use log7. Package
log
implements a simple logging package. It defines a type,Logger
, with methods for formatting output. It also has a predefined ‘standard’ Logger accessible through helper functionsPrint[f|ln]
,Fatal[f|ln]
, andPanic[f|ln]
, which are easier to use than creating a Logger manually. That logger writes to standard error and prints the date and time of each logged message. - The
log.Fatal
function prints the error message and exits the program. - For web related http functionality, we import the package net/http8. Any functions within that we refer as
http.function_name
. - Package os9 provides a platform-independent interface to operating system functionality.
- In our
main()
function, we are setting a variableresp
and doing aGET
request to the Reddit API on our chosen Subreddit. - The
func Get(url string) (resp *Response, err error)
issues a GET to the specified URL. Whenerr
isnil
,resp
always contains a non-nilresp.Body
. Caller should closeresp.Body
when done reading from it. Theresp
is of type Response10. - We use the
defer
function to clean up after the HTTP request, and this call will only be executed after the function returns. - In our Error Handling, check that the HTTP server returns a “200 OK” response. If not, bail, printing the HTTP status message (“500 Internal Server Error”, for example).
- The package
net/http
defines many constants11. - _ is a blank identifier which can be used when we don’t care about a particular return value.
- Finally, we copy the
resp.Body
(filled with the JSON received from the Reddit API) to theos.Stdout
. Theresp.Body
type implementsio.Reader
andos.Stdout
implementsio.Writer
.
Now you can run the program with the go tool:
$
cd
$GOPATH
/src/github.com/SatishTalim/redditnews
$
go run redditnews.go
When you run the program we are outputting (through os.Stdout
) a huge blob of JSON that we received from the Golang Subreddit. Although we can actually see the Articles inside there, this is no good to us. We want to receive the Article’s Title, Author’s name, a Link to the Article, and we want to assess the value of the article, based on the Reddit link Score the Article has received.
- https://github.com/↩
- https://try.github.io/levels/1/challenges/1↩
- https://help.github.com/articles/set-up-git↩
- https://news.ycombinator.com/item?id=9315144↩
- https://addons.mozilla.org/en-us/firefox/addon/jsonview/↩
- http://golang.org/pkg/io/↩
- http://golang.org/pkg/log/↩
- http://golang.org/pkg/net/http/↩
- http://golang.org/pkg/os/↩
- http://golang.org/pkg/net/http/#Response↩
- http://golang.org/pkg/net/http/#pkg-constants↩
Appendix C - Build, deploy webapps to cloud
Complete Appendix A
I am assuming that you have completed Appendix A.
A basic Go web app
Our objective here is to build a web app in Go. In the following program, we shall start a simple service on our local machine. We will have the program running on some port number. When you access the local machine at http://localhost:port_no
(which is the same as http://127.0.0.1:port_no
), it will print a text message on the screen.
Make a new folder and cd
to it as follows:
1
$
mkdir $GOPATH
/src/github.com/SatishTalim/webapp
2
$
cd
$GOPATH
/src/github.com/SatishTalim/webapp
Note: Replace SatishTalim
with your GitHub name.
In this folder, create a file named webapp.go
, open it in your favorite editor, and add the following lines:
1
package
main
2
3
import
(
4
"fmt"
5
"log"
6
"net/http"
7
"os"
8
)
9
10
func
main
()
{
11
http
.
HandleFunc
(
"/"
,
handler
)
12
13
err
:=
http
.
ListenAndServe
(
GetPort
(),
nil
)
14
if
err
!=
nil
{
15
log
.
Fatal
(
"ListenAndServe: "
,
err
)
16
}
17
}
18
19
func
handler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
20
fmt
.
Fprintf
(
w
,
"Hello. This is our first Go web program!"
)
21
}
22
23
// Get the Port from the environment so we can run on Heroku
24
func
GetPort
()
string
{
25
var
port
=
os
.
Getenv
(
"PORT"
)
26
// Set a default port if there is nothing in the environment
27
if
port
==
""
{
28
port
=
"4747"
29
fmt
.
Println
(
"INFO: No PORT environment variable detected, defaulting to "
+
port
)
30
}
31
return
":"
+
port
32
}
- like all Go programs that need to be executed, our program has a package
main
. - we import the fmt1 and net/http2 packages from the Go standard library.
- to work with some printing functions, we import the package
fmt
. - package os3 provides a platform-independent interface to operating system functionality.
- for web related http functionality, we import the package http4. Any functions within that we refer as
http.function_name
. - the first thing we need to do is tell our server what to do when someone comes to our homepage. So, we need to have a request handler at root (“/”). Within the
main
program, we redirect any incoming requests to thehandler
function. We do this by callinghttp.HandleFunc
and passing it two parameters - the first one is a part of the incoming url, and the second is the method capable of handling it. - it then calls
http.ListenAndServe
, specifying that it should listen on port returned by our local functionGetPort
which internally callsGetenv
(retrieves the value of the environment variable named by the key. It returns the value, which will be empty if the variable is not present) on any interface. (Don’t worry about its second parameter,nil
, for now.) This function will block until the program is terminated. - on an error we use log5. Package
log
implements a simple logging package. It defines a type,Logger
, with methods for formatting output. It also has a predefined ‘standard’ Logger accessible through helper functionsPrint[f|ln]
,Fatal[f|ln]
, andPanic[f|ln]
, which are easier to use than creating a Logger manually. That logger writes to standard error and prints the date and time of each logged message. - the function
handler
is of the typehttp.HandlerFunc
. It takes anhttp.ResponseWriter
and anhttp.Request
as its arguments. - when a user connects, the program responds with a text that is sent back to the browser. The
http.ResponseWriter
value assembles the HTTP server’s response; by writing to it, we send data to the HTTP client. - an
http.Request
is a data structure that represents the client HTTP request. - all the parameters of a request can be received via the parameter
http.Request
in thehandler
. You can get the URL, the input values and other details.
Now you can build and install that program with the go
tool:
$
cd
$GOPATH
/src/github.com/SatishTalim/webapp
$
go install
You can now run the program by typing:
$
webapp
listening...
INFO: No PORT environment variable detected, defaulting to 4747
If you run this program and access the URL: http://localhost:47476 the program would present a page containing:
Hello
.
This
is
our
first
Go
web
program
!
Great, it worked!
Static Sites with Go
A web application7 or web app is any application software that runs in a web browser or is created in a browser-supported programming language (such as the combination of JavaScript, HTML and CSS) and relies on a common web browser to render the application.
HTTP means HyperText Transfer Protocol. HTTP uses a simple conversation pattern: the client connects to the server, initiates the dialog by asking the server for something, the server then tries to provide the client with an answer (back). The basic principle of HTTP, and the Web in general, is that every resource (such as a web page) available on the Web has a distinct Uniform Resource Locator (URL), and that web clients can use HTTP verbs such as GET
, POST
, PUT
, and DELETE
to retrieve or otherwise manipulate those resources.
Our objective is to build a web app in Go to display a static site, which we call “Dosa Diner”.
Dosa is a fermented crepe or pancake made from rice batter and black lentils. This staple dish is widely popular in all southern Indian states Karnataka, Andhra Pradesh, Tamil Nadu and Kerala, as well as being popular in other countries like Sri Lanka, Malaysia and Singapore.
To build this static site, as mentioned before, you should have an understanding of basic web technologies (HTTP, HTML). In the following program, we shall start a simple service on our local machine. We will have the program running on port 3000. When you access the local machine at http://localhost:47478 (which is the same as http://127.0.0.1:47479), it will display a static site on the screen. Start by creating the folders to hold the project:
c:\go_projects
\---go
\---src
\---github.com
\---SatishTalim
\---dosasite
| dosasite.go
|
\---public
| index.html
|
+---images
| dosa.jpg
|
\---stylesheets
dosasite.css
We will write our Go code in the file dosasite.go
and some sample HTML and CSS files in a public
folder.
1
<!DOCTYPE html>
2
<html>
3
4
<head>
5
<title>
Dosa Diner</title>
6
<meta
charset=
"utf-8"
>
7
<link
rel=
"stylesheet"
href=
"stylesheets/dosasite.css"
>
8
</head>
9
10
<body>
11
<h1><img
src=
"images/dosa.jpg"
alt=
"Dosa Diner"
/>
Dosa Diner</h1>
12
13
<h2>
The Restaurant</h2>
14
<p>
The Dosa Diner offers casual lunch and dinner fare in a hip atmosphere.
15
The menu changes regularly to highlight the freshest ingredients.</p>
16
17
<h2>
Catering Services</h2>
18
<p>
You have fun... we'll do the cooking. Dosa Diner Catering can handle events
19
from snacks for bridge club to elegant corporate fundraisers.</p>
20
21
<h2>
Location and Hours</h2>
22
<p>
Deccan Corner in Pune, India;
23
Monday through Thursday 11am to 9pm, Friday and Saturday, 11am to midnight.</p>
24
</body>
25
26
</html>
1
body
{
2
background-color
:
#C2A7F2
;
3
font-family
:
sans-serif
;
4
}
5
h1
{
6
color
:
#2A1959
;
7
border-bottom
:
2px
solid
#2A1959
;
8
}
9
h2
{
10
color
:
#474B94
;
11
font-size
:
1.2em
;
12
}
13
h2
,
p
{
14
margin-left
:
120px
;
15
}
Once the above files are created, the code we need to get up and running is quite compact:
1
package
main
2
3
import
(
4
"fmt"
5
"log"
6
"net/http"
7
"os"
8
)
9
10
func
main
()
{
11
fs
:=
http
.
FileServer
(
http
.
Dir
(
"public"
))
12
http
.
Handle
(
"/"
,
fs
)
13
14
fmt
.
Println
(
"Listening..."
)
15
err
:=
http
.
ListenAndServe
(
GetPort
(),
nil
)
16
if
err
!=
nil
{
17
log
.
Fatal
(
"ListenAndServe: "
,
err
)
18
return
19
}
20
}
21
22
// Get the Port from the environment so we can run on Heroku (more of this later)
23
func
GetPort
()
string
{
24
var
port
=
os
.
Getenv
(
"PORT"
)
25
// Set a default port if there is nothing in the environment
26
if
port
==
""
{
27
port
=
"4747"
28
fmt
.
Println
(
"INFO: No PORT environment variable detected, defaulting to "
+
port
)
29
}
30
return
":"
+
port
31
}
- like all Go programs that need to be executed, our program has a package
main
. - we import the
fmt
10 andnet/http
11 packages from the Go standard library. - to work with some printing functions, we import the package
fmt
. - for web related http functionality, we import the package
http
. Any functions within that we refer ashttp.function_name
. - we use the FileServer12 function to create a handler that responds to HTTP requests with the contents of a given FileSystem13.
- we use the operating system’s file system implementation by
http.Dir
- we’ve used the
public
folder relative to our application, but you could use any other folder on your system (or indeed anything that implements theFileSystem
interface). - we use the Handle14 function to register it as the handler for all requests, and launch the server listening on port 4747.
Now you can run program with the go
tool:
$
cd
$GOPATH
/src/github.com/SatishTalim/dosasite
$
go run dosasite.go
Now open http://localhost:4747/index.html15 in your browser. You should see the HTML page we have made.

Deploying Go Web Apps to Heroku
There are plenty of definitions for “cloud computing” online, and for the most part, they generally point to the same thing: taking applications and running them on infrastructure other than your own. Companies or individuals who offload or effectively “outsource” their hardware and/or applications are running those apps “in the cloud.”
Cloud Computing Service Levels
In the figure below, you can see how the analyst firm Gartner segregates cloud computing into three distinct classes of service.

SaaS
Let’s start at the highest level: software applications that are only available online fall into the “Software-as-a-Service” category, also known as “SaaS”. The simplest example to understand is e-mail. For personal e-mail, people typically select from a variety of free web-based e-mail servers such as Google’s Gmail, Yahoo!Mail, or Microsoft’s Hotmail, rather than setting up all of the above through their provider. Not only is it “free” (supported through advertising), but users are freed from any additional server maintenance. Because these applications run (and store their data online), users no longer need to worry about managing, saving, and backing up their files. Of course, now it becomes Google’s responsibility to ensure that your data is safe and secure. Other examples of SaaS include Salesforce, IBM’s NetSuite, and online games.
IaaS
On the opposite end of the spectrum, we have “Infrastructure-as-a-Service,” or “IaaS,” where you outsource the hardware. In such cases, it’s not just the computing power that you rent; it also includes power, cooling, and networking. Furthermore, it’s more than likely that you’ll need storage as well. Generally IaaS is this combination of compute and cloud storage.
When you choose to run your applications at this cloud service level, you’re responsible for everything on the stack that is required to operate above it. By this, we mean necessities such as the operating system followed by additional (yet optional services) like database servers, web servers, load-balancing, monitoring, reporting, logging, middleware, etc. Furthermore, you’re responsible for all hardware and software upgrades, patches, security fixes, and licensing, any of which can affect your application’s software stack in a major way.
PaaS
In the middle, we have “Platform-as-a-Service,” or “PaaS.” At this service level, the vendor takes care of the underlying infrastructure for you, giving you only a platform with which to (build and) host your application(s). Gone are the hardware concerns of IaaS, yet with PaaS, you control the application — it’s your code — unlike as the SaaS level where you’re dependent on the cloud software vendor. The only thing you have to worry about is your application itself.
Systems like Google App Engine, Salesforce’s Heroku and force.com, Microsoft Azure, and VMwares Cloud Foundry, all fall under the PaaS umbrella.
A number of Platform-as-a-Service (PaaS) providers16 allow you to use Go applications on their clouds.
Heroku17 is a new approach to deploying web applications18. Forget about servers; the fundamental unit is the app. Develop locally on your machine just like you always do. When you’re ready to deploy, use the Heroku client gem to create your application in their cloud, then deploy with a single git push. Heroku has full support for Go applications.
We shall soon see how we can deploy an app to Heroku.
Program webapp_h.go
Inside the folder $GOPATH/src/github.com/SatishTalim
create the folder webapp_h
.
Next inside the webapp_h
folder, create a file named webapp_h.go
, and give it the following contents:
1
package
main
2
3
import
(
4
"fmt"
5
"log"
6
"net/http"
7
"os"
8
)
9
10
func
main
()
{
11
http
.
HandleFunc
(
"/"
,
handler
)
12
fmt
.
Println
(
"listening..."
)
13
err
:=
http
.
ListenAndServe
(
GetPort
(),
nil
)
14
if
err
!=
nil
{
15
log
.
Fatal
(
"ListenAndServe: "
,
err
)
16
}
17
}
18
19
func
handler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
20
fmt
.
Fprintf
(
w
,
"Hello. This is our first Go web app on Heroku!"
)
21
}
22
23
// Get the Port from the environment so we can run on Heroku
24
func
GetPort
()
string
{
25
var
port
=
os
.
Getenv
(
"PORT"
)
26
// Set a default port if there is nothing in the environment
27
if
port
==
""
{
28
port
=
"4747"
29
fmt
.
Println
(
"INFO: No PORT environment variable detected, defaulting to "
+
port
)
30
}
31
return
":"
+
port
32
}
This Go package responds to any request by sending a response containing the message:
Hello. This is our first Go web app for Heroku!
Create an account on Heroku
Please ensure that you are connected to the internet and then create an account on Heroku (obviously do this only once). If you don’t have one, then signup19. It’s free and instant. A free account can have upto 5 apps without registering your credit card.
Note: I just read on reddit that Heroku is moving away from free dynos20, which is sad.
Install the Heroku Toolbelt
The Heroku Toolbelt21 ensures that you have access to the Heroku command-line client, Foreman which is used to run applications locally, and the Git revision control system that is used to deploy sites to Heroku.
Once installed, you’ll have access to the heroku
command from your command window. Log in using the email address and password you used when creating your Heroku account:
$
cd
$GOPATH
/src/github.com/SatishTalim/webapp_h
$
heroku login
Enter your Heroku credentials.
Email: golangchallenge@gmail.com
Password:
Could not find an existing public key.
Would you like to generate one? [
Yn]
Generating new SSH public key.
Uploading ssh public key /Users/satish/.ssh/id_rsa.pub
Create a Procfile
Create a Procfile
to tell Heroku what command to run for the web
process in our app. The Procfile
should be created in the folder $GOPATH/src/github.com/SatishTalim/webapp_h
and contains:
web
:
webapp_h
Use Git
In order to deploy to Heroku we’ll need the app stored in Git. In the same folder i.e. $GOPATH/src/github.com/SatishTalim/webapp_h
type:
$
git init
$
git add -A .
$
git commit -m "code"
Install Godep
The recommended way to manage Go package dependencies on Heroku is with Godep22, which helps build applications reproducibly by fixing their dependencies.
Before installing Godep
ensure that you have Mercurial23 installed (check that you have an hg
command) and have set the path
environment variable for mercurial for eg. c:\Mercurial
.
Now let us install Godep
:
$
go get github.com/kr/godep
Now save your dependencies:
$
godep save
This will save a list of dependencies to the file Godeps/Godeps.json
, and copy their source code into Godeps/_workspace
.
Add these new files to git:
$
git add -A .
$
git commit -m "dependencies"
Now we’re ready to ship this to Heroku.
Heroku deploy
Create a new Heroku app, telling it to use the Go Heroku Buildpack24 to build your Go code:
$
heroku create -b https://github.com/kr/heroku-buildpack-go.git
Creating tranquil-bastion-1774... done
, stack is cedar
BUILDPACK_URL
=
https://github.com/kr/heroku-buildpack-go.git
http://tranquil-bastion-1774.herokuapp.com/ |
git@heroku.com:tranquil-bastion-1774.git
Git remote heroku added
Push the code to Heroku:
$
git
push
heroku
master
Initializing
repository
,
done
.
Counting
objects
:
11
,
done
.
Delta
compression
using
up
to
4
threads
.
Compressing
objects
:
100
%
(
8
/
8
),
done
.
Writing
objects
:
100
%
(
11
/
11
),
1.29
KiB
|
0
bytes
/
s
,
done
.
Total
11
(
delta
0
),
reused
0
(
delta
0
)
----->
Fetching
custom
git
buildpack
...
done
----->
Go
app
detected
----->
Installing
go1
.2
...
done
----->
Running
:
godep
go
install
-
tags
heroku
.
/
...
----->
Discovering
process
types
Procfile
declares
types
->
web
----->
Compressing
...
done
,
1.7
MB
----->
Launching
...
done
,
v4
http:
//pumpkin-cupcake-1256.herokuapp.com deployed to Heroku
To
git
@
heroku
.
com
:
pumpkin
-
cupcake
-
1256.
git
*
[
new
branch
]
master
->
master
Your app should be up and running. Visit it with:
$
heroku open
Opening pumpkin-cupcake-1256... done
That’s it - you now have a running Go app on Heroku!
Here’s the URL again: http://pumpkin-cupcake-1256.herokuapp.com/25.
A brief note on Heroku
Troubleshooting
A number of bugs can potentially be encountered in the process of deploying a Go application to Heroku. Many times, a bug can be traced to a misunderstanding of Heroku’s architecture and how it works.
Heroku’s Architecture
Overall, Heroku applications are expected to follow the “process model”26 architecture. The process model allows applications, and web applications in particular, to be easily scaled out and managed with little administrative overhead. In exchange, the architecture requires applications to conform to certain rules and restrictions.
There are two fundamental parts to a Heroku application: Dynos, which follow the proccess model; and Add-ons, which are provided through third-party integrations.
Dynos
The main unit of a Heroku application is the Dyno.
Dynos are ephemeral servers which run a single process. Each process started by a dyno must be assigned a “process type”. A dyno’s type is defined by the starting process type.
Even though a dyno can only start a single process, once started that process may spawn additional processes within the dyno within limits27.
Keep in mind, dynos do not retain data. Data does not persist between Dyno restarts and there are no shared volumes between dynos. Practically speaking, you cannot retain any data on a dyno. Add-ons28 should be used for persistence and are explained below.
There are three types29 of Dynos tailored for different workloads: web dynos, worker dynos, and one-off dynos.
Only web dynos receive HTTP traffic via the Heroku router. Heroku assigns a single $PORT
environment variable to all dynos running a web
process type. All inbound HTTP traffic to a web
process goes through the single assigned $PORT
variable. The Heroku router attaches headers30 to HTTP requests to describe the original request as received by Heroku.
Web Dynos also have the following restrictions not discussed above:
- They allow 50 active requests per web
dyno with a 50 request queue.
- They must connect to the Heroku provided $PORT
environment variable within 60 seconds31 or the dyno will automatically be shut down.
Add-ons
Persistence and other functionality needed by applications which cannot be provided by Dynos are available through Add-ons32. Many add-ons are integrated with Dynos through environment variables.
Slugs
In addition to the process described above, Go applications can also be cross-compiled into a binary and deployed to Heroku through the use of a custom slug. Slugs are containers (similar to Docker images) which hold everything needed to run a program on Heroku’s infrastructure. To deploy a Go application this way, a slug needs to be created, published, and released on Heroku. Heroku has published an article specifically about deploying Go slugs33.

The address used in the exercise above is that of a landmark, historic home and office of the United States president.
Exercise 18 Solution:
Exercise 19 Solution:
License
This work is licensed under the Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit here36.
All example code used in this book is hereby put in the public domain.
©Satish Talim - 2015
- http://golang.org/pkg/fmt/↩
- http://golang.org/pkg/net/http/↩
- http://golang.org/pkg/os/↩
- http://golang.org/pkg/net/http/↩
- http://golang.org/pkg/log/↩
- http://localhost:4747↩
- http://en.wikipedia.org/wiki/Web_application↩
- http://localhost:4747↩
- http://127.0.0.1:4747↩
- http://golang.org/pkg/fmt/↩
- http://golang.org/pkg/net/http/↩
- http://golang.org/pkg/net/http/#FileServer↩
- http://golang.org/pkg/net/http/#FileSystem↩
- http://golang.org/pkg/net/http/#Handle↩
- http://localhost:4747/index.html↩
- https://code.google.com/p/go-wiki/wiki/ProviderIntegration↩
- http://heroku.com/↩
- http://mmcgrana.github.io/2012/09/getting-started-with-go-on-heroku.html↩
- http://heroku.com/signup↩
- https://news.ycombinator.com/item?id=9315144↩
- https://toolbelt.heroku.com↩
- https://github.com/kr/godep↩
- http://mercurial.selenic.com/downloads/↩
- https://github.com/kr/heroku-buildpack-go↩
- http://pumpkin-cupcake-1256.herokuapp.com/↩
- https://devcenter.heroku.com/articles/process-model↩
- https://devcenter.heroku.com/articles/dynos#process-thread-limits↩
- https://addons.heroku.com/↩
- https://devcenter.heroku.com/articles/dynos#types-of-dynos↩
- https://devcenter.heroku.com/articles/http-routing#heroku-headers↩
- https://devcenter.heroku.com/articles/dynos#web-dynos↩
- https://addons.heroku.com/↩
- https://devcenter.heroku.com/articles/platform-api-deploying-slugs#go↩
- https://developers.google.com/maps/documentation/geocoding/↩
- https://developers.google.com/maps/documentation/streetview/index↩
- http://creativecommons.org/licenses/by-nc-sa/3.0/↩