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:
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:
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:
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:
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.
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")
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.
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.
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
:
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
:
Run this program, as follows:
The output you will see is something like this:
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:
When we run the program, the output is:
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:
Multiple variables of the same type can also be declared on a single line:
makes x and y both int
variables.
You can also make use of parallel assignment:
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:
generates this error:
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:
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.
3.2.1 Typed and Untyped constants
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:
When we run the program, the output is:
Clear the Go Playground screen of whatever is written there and type:
When we run the program, the output is:
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
Run the above program and the output that you will see is:
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:
The name of the label is case sensitive.
4.3 switch
The general format of the switch is:
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.
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.
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.
Here’s an example:
The output of the program is 4950
.
The above program is available here2.
Here are some code snippets and their explanation:
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:
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.
A nested for loop example:
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:
The output is:
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:
The output is:
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:
The output is:
Few more examples:
The output on my machine is:
Recollect that the built-in function new
allocates memory. new
can be applied to types like int
, string
etc.
The output is:
The output is:
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.
6.1 A function can have multiple return values. Some examples:
Here is another example:
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:
The output is:
6.2 A function can take zero or more arguments
When two or more consecutive named function parameters share a type, you can omit the type from all but the last.
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.
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:
The output when you run the above program is:
The output when you run the above program is:
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:
The output is:
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:
The output is:
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:
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:
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.
The output is:
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:
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:
The output is:
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
:
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:
The output is:
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:
The output is:
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:
The output is:
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.
Another way to create slices is to use the [low : high]
expression:
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
:
The output is:
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
:
The output is:
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.
The output is:
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:
The output is:
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.
The output is:
If you only need the first item in the range (the key or index), drop the second:
If you only need the second item in the range (the value), use the blank identifier, an underscore, to discard the first:
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:
The output is:
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.
The output is:
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:
The output is:
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
:
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:
The output of the program is:
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:
The output is:
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:
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:
Struct fields are accessed using a dot. Here’s an example:
The output is is 4
.
Let us define a Square struct as follows:
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.
The output is:
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:
The output is:
An anonymous struct field
The output is:
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.
The output is:
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:
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:
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:
The output is:
Let’s now modify the program stuct0.go
to use structs.
The output is:
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.
The output is:
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:
Any struct that has a Woof method will implement the Dog interface implicitly and can be used as a Dog.
with output:
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:
with output:
Earlier we had seen an example of a Square
struct
and an Area
function:
with output:
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:
Let us write the full program:
The output is:
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:
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.
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:
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:
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:
Now the output I get is always:
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
:
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:
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
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:
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:
Part of the output is shown below:
17.2.2.1 seek function:
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:
17.2.3.1 Create
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
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:
17.2.4.1 Readdir
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:
17.3.1 NewReader
NewReader
returns a new Reader
whose buffer has the default size.
17.3.2 Peek
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:
The output is:
17.3.3 NewWriter
NewWriter
returns a new Writer
whose buffer has the default size.
Original file contents:
Modified file contents:
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:
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:
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:
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.
The output is:
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
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:
The output is:
What if I don’t want to show the UserName if it is empty? Here’s the modified code:
The output is:
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.
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:
The output is:
Let’s look at another example:
The output is:
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:
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?
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:
The output is:
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.
The output was:
Let’s rewrite the above program using json.NewEncoder
as follows:
The output is the same as before:
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:
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.
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.
The output is:
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.
Also, we write a check
function that checks for errors, if any:
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.
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:
Use a streaming Decoder
Extract the latitude and logitude
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.
Use the Forecast struct to get the results
Here’s the full program:
The output is:
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
To run this program, type:
go run get_info.go www.golang.com:80
The output is:
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.
- 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:
Replace SatishTalim
with your GitHub id.
Next, create a file named mult.go
inside that folder, containing the following Go code:
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.
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:
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:
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:
Run the program as follows:
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:
Replace SatishTalim
with your GitHub id.
Next, create a file named conv_test.go
inside that folder, containing the following Go code:
Run the program as follows:
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
.
Run the program as follows:
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:
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:
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.
(The data flows in the direction of the arrow.)
Channels must be created using the built-in function make
before use:
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:
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:
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:
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:
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:
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
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:
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
:
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:
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:
Then from the folder where you have saved the file hello_world.go
run it with the go
tool:
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.
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:
Next, create a file named hello.go
inside that directory, containing the following Go code:
Now you can build and install that program with the go
tool:
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:
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:
In this folder, create a file named redditnews.go
, open it in your favorite editor, and add the following lines:
- 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:
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:
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:
- 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:
You can now run the program by typing:
If you run this program and access the URL: http://localhost:47476 the program would present a page containing:
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:
We will write our Go code in the file dosasite.go
and some sample HTML and CSS files in a public
folder.
Once the above files are created, the code we need to get up and running is quite compact:
- 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:
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:
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:
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:
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:
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
:
Now save your dependencies:
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:
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:
Push the code to Heroku:
Your app should be up and running. Visit it with:
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/↩