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 all 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!

Why Go Playground?

Go developers use the Playground to share code ideas, test theories, and debug their code, as you soon will too. Every time you create a new application on the Playground, you can click the Share button to get a sharable URL that anyone else can open. Try this one: http://play.golang.org/p/sOqjtiXkRF2

There are limitations to the programs that can be run in the playground, which please be aware:

  • The playground can use most of the standard library, with some exceptions. The only communication a playground program has to the outside world is by writing to standard output and standard error.
  • In the playground the time begins at 2009-11-10 23:00:00 UTC (determining the significance of this date is an exercise for the reader). This makes it easier to cache programs by giving them deterministic output.
  • There are also limits on execution time and on CPU and memory usage.

As such, there are certain programs in the eBook that will not run in the Go Playground.

Program: hello_world.go


1 package main
2 
3 import "fmt"
4 
5 func main() {
6 	fmt.Printf("Hello World")
7 }

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. The fmt 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:

Program: hello.go


 1 // Single line comment. Line comments start with the
 2 // character sequence // and stop at the end of the
 3 // line. A line comment acts like a newline.
 4 // Comments do not nest.
 5 
 6 /*
 7 Multi-
 8  line comment
 9 */
10 
11 /*
12 Package main.
13 All Go files start with package <something>.
14 package main is required for a standalone executable.
15 */
16 package main
17 
18 import "fmt"
19 
20 func main() {
21 	// Multiple statements separated by ;
22 	fmt.Printf("Hello "); fmt.Printf("World\n")
23 
24 	// Single statement then ; not needed
25 	fmt.Printf("Learning Go\n")
26 }

Once the Go Playground loads up, clear whatever is written on the screen and type out the above program.

Click on the Run button and check the output at the bottom of the screen.

Let’s understand this program:

2.1 Comments

Go provides:

  • /* */ are block comments. Block comments appear mostly as package comments, but are useful within an expression or to disable large swaths 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

func new(Type) *Type

The new built-in function allocates memory. The first argument is a type, not a value, and the value returned is a pointer to a newly allocated zero value of that type.

func len(v Type) int

The len built-in function returns the length of v, according to its type:

Array: the number of elements in v. String: the number of bytes in v. if v is nil, len(v) is zero.

print and println are low level printing functions that can be used without reverting to the fmt package. These are mainly used for debugging. For more information on this, please read this3.

2.4 Keywords

This is the set of 25 keywords4 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, then import, 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 " ".
  • A package other than main is commonly called a library. We are using the fmt library.
  • 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 fmt5, 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/6.

If you are importing a single package then you write it like:

import "fmt"

If multiple packages are needed, they can each be imported by a separate statement:

1 import (
2         "fmt"
3         "os"
4 )

Only apply this when there is more than one entry; in that case it is also clearer to list the package names in alphabetical order.

Note: importing a package which is not used in the rest of the code is a build-error (for example: imported and not used: os).

Unused variables or imports

The presence of an unused variable may indicate a bug, while unused imports just slow down compilation, an effect that can become substantial as a program accumulates code and programmers over time. For these reasons, Go refuses to compile programs with unused variables or imports, trading short-term convenience for long-term build speed and program clarity.

The reason for having no warnings. If it’s worth complaining about, it’s worth fixing in the code. And if it’s not worth fixing, it’s not worth mentioning.

Nowadays, most Go programmers use a tool, goimports7, which automatically rewrites a Go source file to have the correct imports, eliminating the unused imports issue in practice.

imports8 are organized in groups, with blank lines between them. The standard library packages are in the first group. For example:

 1 package main
 2 
 3 import (
 4         "fmt"
 5         "hash/adler32"
 6         "os"
 7 
 8         "appengine/user"
 9         "appengine/foo"
10 
11         "code.google.com/p/x/y"
12         "github.com/foo/bar"
13 )

The above is a convention.

  • After importing a package, you can refer to the names it exports.
  • In Go, a name is exported if it begins with a capital letter. Foo is an exported name, as is FOO. The name foo is not exported. A concrete example is in our use of fmt.Println, note the Println 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. For instance, 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:

txt, _ := tpl.New("test").Parse("Hello {{.Name}}")

Aliases are a great (and necessary) feature of Go package management. But don’t go overboard. Conventionally, aliases are only used when necessary (such as when package names would otherwise collide). Code is more readable without unnecessary aliases.

2.6.2 Removing explicit package reference

We might choose to simplify matters a little by importing the fmt namespace into the namespace of the current source file, which requires we change our import statement.

import . "fmt"

This consequently allows the explicit package reference to be removed from the Println() function call.

Println("hello world")

1 package main
2 
3 import . "fmt"
4 
5 func main() {
6         Println("hello world")
7 }

Exercise 1

What is wrong with the following program?

Program: what_error.go


1 package main
2 
3 import "fmt"
4 import "os"
5 
6 func main() 
7 {
8 	fmt.println("Hello, world.")
9 }

Please use this link9 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.
  • 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.
  • main has no parameters and no return type.
  • fmt.Printf("") here we call a function Printf from the package fmt to print a string to the screen.
  • In Go, we use a dot notation to access the function Printf of the package fmt.

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. UTF-8 is a character encoding capable of encoding all possible characters (called code points) in Unicode.

The character encoding scheme ASCII comprises 128 code points, Extended ASCII comprises 256 code points and Unicode comprises 1,114,112 code points.

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.

int64 is big enough to allow accuracy of millionths of a cent.

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 in 0.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 math10 package’s Inf11, NaN12, IsInf13, and IsNaN14 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.

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
String "" (empty string)

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 sheet15

Let’s look at an example.

Program: format.go


 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6         // Formatting booleans
 7         fmt.Printf("%v\n", false) 
 8         fmt.Printf("%t\n", true)
 9 
10         // Formatting integers
11         fmt.Printf("%v\n", 987)
12         fmt.Printf("%d\n", 345)
13 
14         // Formatting floats
15         fmt.Printf("%v\n", 987123456.987345123432)
16         fmt.Printf("%f\n", 987123456.987345123432)
17         // Use %g for large exponents 
18         fmt.Printf("%g\n", 987123456.987345123432)
19 
20         // When formatting numbers you will often 
21         // want to control the width and precision 
22         // of the resulting figure. To specify the 
23         // width of an integer, use a number after 
24         // the % in the verb. By default the result 
25         // will be right-justified and padded with 
26         // spaces.
27         fmt.Printf("|%6d|%6d|\n", 23, 987)
28 
29         // You can also specify the width of printed
30         // floats, though usually you'll also want to
31         // restrict the decimal precision at the same
32         // time with the width.precision syntax.
33         fmt.Printf("|%6.2f|%6.2f|\n", 3.2, 4.37)
34 
35         // To left-justify, use the - flag.
36         fmt.Printf("|%-6.2f|%-6.2f|\n", 3.2, 4.37)
37 
38         // So far we’ve seen Printf, which prints 
39         // the formatted string to os.Stdout. 
40         // Sprintf formats and returns a string 
41         // without printing it anywhere.
42         s := fmt.Sprintf("a %s", "string")
43         fmt.Println(s)
44         
45         fmt.Println(2001,3456)
46         fmt.Print(20,30)
47 }

Once the Go Playground loads up, clear whatever is written on the screen and type out the above program.

Click on the Run button and check the output at the bottom of the screen.

false
true
987
345
9.871234569873451e+08
987123456.987345
9.871234569873451e+08
|    23|   987|
|  3.20|  4.37|
|3.20  |4.37  |
a string
2001 3456
20 30

2.11 Console input

The functions Scan, Scanf and Scanln from the fmt package read from os.Stdin.

Scan scans text read from standard input, storing successive space-separated values into successive arguments. Newlines count as space. It returns the number of items successfully scanned.

Scanf scans text read from standard input, storing successive space-separated values into successive arguments as determined by the format. It returns the number of items successfully scanned. Scanf parses the arguments according to a format string, analogous to that of Printf with certain restrictions.

Scanln is similar to Scan, but stops scanning at a newline and after the final item there must be a newline or EOF.

Write the following program and store it in the same folder where you had stored hello.go:

Program: ci.go


 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6         var username string
 7 
 8         fmt.Println("Enter Username: ")
 9         fmt.Scanf("%s", &username)
10 
11         fmt.Println("Username: ", username)
12 }

For Windows users, use fmt.Scanf("%s\n", &username) instead of fmt.Scanf("%s", &username).

Run the program.

We shall learn about var and the & operator in later chapters.

2.12 Command-line arguments - Args

For the programs in this section, please download, install and setup Go as per Appendix A. Do not use the Go Playground.

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:

Program: cmd_line_args.go


1 package main
2 
3 import "fmt"
4 import "os"
5 
6 func main() {
7         //You can get individual args with normal indexing
8         fmt.Println(os.Args[1], os.Args[2], os.Args[3], os.Args[4])
9 }

Run this program, as follows:

go run cmd_line_args.go a1 a2 a3 a4

The output you will see is something like this:

a1 a2 a3 a4

Remember:

Opening brace can’t be placed on a separate line

func main()  
{ //error, can't have the opening brace on a separate line
          fmt.Println("Hello world!")
}

Unused variables

If you have an unused variable your code will fail to compile. There’s an exception though. You must use variables you declare inside functions, but it’s OK if you have unused global variables. If you assign a new value to the unused variable your code will still fail to compile. You need to use the variable value.

package main

var gvar int //not an error

func main() {  
        var one int   //error, unused variable
        two := 2      //error, unused variable
        var three int //error, even though it's assigned 3 on the next line
        three = 3     
}

Unused imports

Your code will fail to compile if you import a package without using any of its exported functions, interfaces, structures, or variables.

package main

import "fmt"

func main() {
}

The compiler error is:

main.go:3: imported and not used: "fmt"

2.13 Solutions

Exercise 1 Reasons16.

  1. https://play.golang.org/
  2. http://play.golang.org/p/sOqjtiXkRF
  3. http://golang.org/ref/spec#Bootstrapping
  4. http://golang.org/ref/spec#Keywords
  5. http://golang.org/pkg/fmt/
  6. http://golang.org/pkg/
  7. https://godoc.org/golang.org/x/tools/cmd/goimports
  8. https://github.com/golang/go/wiki/CodeReviewComments#imports
  9. http://play.golang.org/p/9vq29NXfno
  10. https://golang.org/pkg/math/
  11. https://golang.org/pkg/math/#Inf
  12. https://golang.org/pkg/math/#NaN
  13. https://golang.org/pkg/math/#IsInf
  14. https://golang.org/pkg/math/#IsNaN
  15. http://golang-examples.tumblr.com/post/86795367134/fmt-printf-format-reference-cheat-sheet
  16. http://play.golang.org/p/7Ofcns7kWR