Table of Contents
- Preface
- How to use this eBook
- 1. Introduction
- 2. Learn Go programming
- 3. Variables, Constants, and Types
- 4. Control Structures
- 5. Pointers
- 6. More on Functions
- 7. Detailed information
- 8. Arrays
- 9. Slices
- 10. Range
- 11. Maps
- 12. Structs
- 13. interface
- 14. Error
- 15. io, os and net/http - Getting started
- 16. Random Numbers
- 17. File Handling
- 18. JSON and Go
- 19. TCP programming using Go
- 20. Unit testing and Benchmarking Go programs
- 21. Concurrency
- 22. Additional Exercises
- 23. Additional Reading
- Appendix A
- Appendix B - Project: redditnews for Baby Gophers
- Appendix C - Build, deploy webapps to cloud
Preface
Who is the eBook for?
These are the study notes, in eBook form, I made when I learned Go programming and is an introduction to the Go language. The intended audience are people who are familiar with programming and know some programming language. This does not teach you how to program, rather it teaches you how to use Go.
What will you learn?
In the end, you will understand and know the basics of the Go language.
Using code examples
All of the code in this eBook can be used pretty much anywhere and anyway you please.
Errata
Although I have taken every care to ensure the accuracy of the content of this eBook, mistakes do happen. If you find a mistake in this eBook — maybe a mistake in the text or the code — I would be grateful if you would report this to me. By doing so, you can save other readers from frustration and help me improve subsequent versions of this eBook. If you find any errata, please report them by sending me an email.
Acknowledgements
I have made extensive references to the following:
- How to Write Go Code1
- A Tour of Go2
- Go Language Specification3
- Effective Go4
- Go FAQ5
- 50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs6
I’d like to thank the Go community who have written excellent articles, blog posts on Go. I have referred to many such articles and blog posts, while I learned Go, and given them due credit in this eBook.
There are a good number of people who deserve thanks for their help and support they provided, either while or before this eBook was written, and there are still others whose help will come after the eBook is released. For help with proof reading, checking exercises and text improvements, I would like to thank Anders Persson7, Basant Singh8, Christian Hujer, Gunnar Aasen, Jose Carlos Monteiro9, Kevin Lozandier10, Kiran Mysore11, Kirit Ayya12, Marcelo Magallon13, Nilesh Govindrajan14, Peggy Li15, Prathamesh Sonpatki16, Shawn Milochik, Valindo Godinho17 and Victor Goff18.
The Gopher character is based on the Go mascot designed by Renée French19 and copyrighted under the Creative Commons Attribution 3.0 license.
How to contact me?
I can be reached via e-mail at satish.talim@gmail.com. Please contact me if you have any questions, comments, kudos or criticism on the eBook. Constructive criticism is definitely appreciated; I want this eBook to get better through your feedback.
Thanks
Thanks for downloading and checking out this eBook. As part of the lean publishing philosophy, you’ll be able to interact with me as the eBook is completed. I’ll be able to change things, reorganize parts, and generally make a better eBook. I hope you enjoy.
- https://golang.org/doc/code.html↩
- https://tour.golang.org/welcome/1↩
- https://golang.org/ref/spec↩
- https://golang.org/doc/effective_go.html↩
- http://golang.org/doc/faq#unused_variables_and_imports↩
- http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/↩
- https://twitter.com/uandersp↩
- https://twitter.com/SinghBasant↩
- https://github.com/straider↩
- https://github.com/lozandier↩
- https://twitter.com/techgravity↩
- https://twitter.com/kirit_ayya↩
- https://github.com/mem↩
- https://twitter.com/nileshgr↩
- https://twitter.com/_peggyli↩
- https://twitter.com/_cha1tanya↩
- https://twitter.com/Valindogodinho↩
- https://twitter.com/kotp↩
- http://reneefrench.blogspot.in/↩
How to use this eBook
With the help of this eBook, you should:
- Go through each exercise.
- Type in each sample exactly.
- Make it run.
If you go through this eBook, and do each exercise for one or two hours a night, you will have a good foundation for moving onto another book about Go to continue your studies. This eBook won’t turn you into a Go expert overnight, but it will get you started on the path to learning how to code in Go.
Remember:
- Typing the code samples and getting them to run will help you learn the names of the symbols, get familiar with typing them, and get you reading the language.
- You must pay attention to the tiniest details of your work or you will miss important elements of what you create. By going through this eBook, and copying each example exactly, you will be training your brain to focus on the details of what you are doing, as you are doing it.
- While you do these exercises, typing each one in, you will be making mistakes. It’s inevitable; even seasoned programmers would make a few. Your job is to compare what you have written to what’s required, and fix all the differences. By doing so, you will train yourself to notice mistakes, bugs, and other problems.
- You must type each of these exercises in, manually. If you copy and paste, you might as well not even do them. The point of these exercises is to train your hands, your brain, and your mind in how to read, write, and see code. If you copy-paste, you are cheating yourself out of the effectiveness of the lessons.
I know that to get good at anything you have to practice every day, even if it’s difficult. Keep trying and eventually it’ll be easier and fun.
If you break the problem down into small exercises and lessons, and do them every day, you can learn to do almost anything. If you focus on slowly improving and enjoying the learning process, then you will benefit no matter how good you are at it.
Remember whatever your reason for wanting to quit, don’t. Victor Goff and I are available on the slack channel1 to help you in the learning process.
- https://docs.google.com/forms/d/1WXO68p3gH4b-4S3dOS_MUbvoe7uaRNT9tii1syTznYA/viewform↩
1. Introduction
1.1 What is Go?
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.
Go comes with a great standard library and an enthusiastic community.
1.2 Go Recent History
The history of the language before its first release, back to 2007, is covered in the language’s FAQ2.
The mascot and logo were designed by Renée French3. The logo and mascot are covered by the Creative Commons Attribution 3.0 license.
The current version of Go is 1.5. Development will continue on Go itself, but the focus will be on performance, reliability, portability and the addition of new functionality such as improved support for internationalization.
1.3 Go Authors
Go was initially developed at Google in 2007 by Robert Griesemer4, Rob Pike5 and Ken Thompson6.
Go became a public open source project on November 10, 2009. Many people from the community have contributed ideas, discussions, and code.
1.4 Why Go?
Very briefly:
- Compiled, instead of interpreted or byte-coded for a virtual machine
- Fast, to compile and to execute
- Safe because it’s Statically Typed, instead of dynamically typed
- C-like Syntax, but much cleaner and simpler (less noise, less verbose), with a self-imposed convention for its Coding Style
- Type Inference and Postfix Types
- Garbage Collector
- UTF-8 encoding mode, by default
- Concurrency, with Channels and GoRoutines
- Open Source
2. Learn Go programming
This eBook is an introduction to the Go language. The intended audience are people who are familiar with programming and know some programming language. This eBook does not teach you how to program, rather it teaches you how to use Go.
We shall be using the Go Playground1 to write and run most of the programs in this eBook.
I have purposely left the details of installing Go on your local computer and its various settings in Appendix A, as I first want you to get your hands wet with writing Go code!
There are limitations to the programs that can be run in the playground, which please be aware.
As such, there are certain programs in the eBook that will not run in the Go Playground. I shall tell you which programs won’t run.
Once the Go Playground loads up, clear whatever is written on the screen and type our first program:
1 package main
2
3 import "fmt"
4
5 func main() {
6 fmt.Printf("Hello World")
7 }
Click on the Run button and you will see “Hello World” at the bottom of the screen.
Let’s quickly understand the above program:
- Go programs are organized as packages.
- The
importstatement allows you to use external code. Thefmtpackage, in this case, which is provided by the standard library allows you to format and print data. - The
main()function is what gets executed when you run your application - just like in C.
Let’s look at another bigger program in more detail:
1 // Single line comment. Line comments start with the
2 // character sequence // and stop at the end of the
3 // line. A line comment acts like a newline.
4 // Comments do not nest.
5
6 /*
7 Multi-
8 line comment
9 */
10
11 /*
12 Package main.
13 All Go files start with package <something>.
14 package main is required for a standalone executable.
15 */
16 package main
17
18 import "fmt"
19
20 func main() {
21 // Multiple statements separated by ;
22 fmt.Printf("Hello "); fmt.Printf("World\n")
23
24 // Single statement then ; not needed
25 fmt.Printf("Learning Go\n")
26 }
Once the Go Playground loads up, clear whatever is written on the screen and type out the above program.
Click on the Run button and check the output at the bottom of the screen.
Let’s understand this program:
2.1 Comments
Go provides:
-
/* */are block comments. Block comments appear mostly as package comments, but are useful within an expression or to disable many lines of code. - Line comments start with the
//character sequence and stop at the end of the line. - A line comment acts like a newline.
- Comments do not nest.
Line comments are the norm.
2.2 Statements
The Go code is structured in statements. A statement doesn’t need to end with a ;. The Go compiler automatically inserts semicolons at the end of statements. However if multiple statements are written on one line (a practice which is not encouraged for readability reasons), they must be separated by ;.
For more information, please read “Semicolons” in a later chapter.
2.3 Identifiers
Identifiers name program entities such as variables and types.
- An identifier is a sequence of one or more letters and digits.
- The first character in an identifier must be a letter.
- Identifiers are case-sensitive.
2.3.1 Blank identifier
The _ itself is a special identifier, called the blank identifier. It can be used in declarations or variable assignments like any other identifier (and any type can be assigned to it), but its value is discarded, so it cannot be used anymore in the code that follows.
2.3.2 Pre-declared identifiers
The following identifiers are implicitly declared:
| Types | bool, byte, complex64, complex128, error, float32, |
| float64, int, int8, int16, int32, int64, rune, string, | |
| uint, uint8, uint16, uint32, uint64, uintptr | |
| Constants | true, false, iota |
| Null or no value | nil |
| Functions | append, cap, close, complex, copy, delete, imag, len, |
| make, new, panic, print, println, real, recover |
2.4 Keywords
This is the set of 25 keywords5 or reserved words and may not be used as identifiers.
| break | default | func | interface | select |
| case | defer | go | map | struct |
| chan | else | goto | package | switch |
| const | fallthrough | if | range | type |
| continue | for | import | return | var |
2.5 package
- All Go files start with
package <something>. - In Go,
packageis always first, thenimport, then everything else. -
package mainis 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
-
importdeclaration declares library packages referenced in this file. - The package names are enclosed within
"". - Apart from package
mainwe are also using thefmtpackage. - A Go program is created by linking together a set of packages through the
importkeyword. -
import "fmt"tells Go that this program needs (functions, or other elements, from) the package fmt6, which implements functionality for formatted IO. -
importloads the public declarations from the compiled package, it does not insert the source code. - The Go installation contains a number of ready-to-use packages, which form the standard library. The entire standard library is documented, with examples, at http://golang.org/pkg/7.
If you are importing a single package then you write it like:
import "fmt"
If multiple packages are needed, they can each be imported by a separate statement:
1 import (
2 "fmt"
3 "os"
4 )
Only apply this when there is more than one entry; in that case it is also clearer to list the package names in alphabetical order.
Note: importing a package which is not used in the rest of the code is a build-error (for example: imported and not used: os).
imports10 are organized in groups, with blank lines between them. The standard library packages are in the first group. For example:
1 package main
2
3 import (
4 "fmt"
5 "hash/adler32"
6 "os"
7
8 "appengine/user"
9 "appengine/foo"
10
11 "code.google.com/p/x/y"
12 "github.com/foo/bar"
13 )
The above is a convention.
- After importing a package, you can refer to the names it exports.
- In Go, a name is exported if it begins with a capital letter.
Foois an exported name, as isFOO. The namefoois not exported. A concrete example is in our use offmt.Println, note thePrintlnwith “P” not “p”. - In Go, the use of the words public and private is really not accurate. It is more accurate to say an identifier is exported or not exported from a package.
- When an identifier is exported from a package, it means the identifier can be directly accessed from any other package in the code base.
- When an identifier is not exported from a package, it can’t be directly accessed from any other package.
After the import statement, zero or more constants (const), variables (var), and types (type) can be declared; these are global (have package scope) and are known to all functions in the code, and they are followed by one or more functions (func).
2.6.1 Aliasing of imports
Go allows the aliasing of imports.
1 import tpl "text/template"
2 txt, err := tpl.New("test").Parse("Hello {{.Name}}")`
The code tpl "text/template" imports the text/template package under the name tpl.
Then the application can call functions in text/templete under the alias name, as shown above.
Aliases are a great (and necessary) feature of Go package management. But don’t go overboard. Conventionally, aliases are only used when necessary (such as when package names would otherwise collide). Code is more readable without unnecessary aliases.
2.6.2 Removing explicit package reference
We might choose to simplify matters a little by importing the fmt namespace into the namespace of the current source file, which requires we change our import statement.
import . "fmt"
This consequently allows the explicit package reference to be removed from the Println() function call.
Println("hello world")
1 package main
2
3 import . "fmt"
4
5 func main() {
6 Println("hello world")
7 }
1 package main
2
3 import "fmt"
4 import "os"
5
6 func main()
7 {
8 fmt.println("Hello, world.")
9 }
Please use this link13 to run the program.
2.7 functions
-
func main() {is a function definition.mainis special. It is the entry point for the executable program. - The requirements for the compiler to produce an executable are belonging to
package mainand declaring themain()function. This function receives no inputs and returns no result, it’s more like a “procedure” in some other languages. - Thus
mainhas 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 functionPrintffrom the packagefmtto print a string to the screen. - In Go, we use a dot notation to access the function
Printfof 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.99results in0.020000000000000018– A number extremely close to what we would expect, but not exactly the same. We can, of course, represent 0.02 with no difficulty. - Like integers, floating point numbers have a certain size (32 bit or 64 bit). Using a larger sized floating point number increases it’s precision (how many digits it can represent).
- There are no constants denoting the IEEE-754 infinity and not-a-number values, but the math14 package’s Inf15, NaN16, IsInf17, and IsNaN18 functions return and test for those values at run time.
Go has two floating point types: float32 and float64 (also often referred to as single precision and double precision respectively) as well as two additional types for representing complex numbers (numbers with imaginary parts): complex64 and complex128.
Floating point precision is finite and you should always be very cautious when making comparisons on real numbers.
Generally, we should stick with float64 when working with floating point numbers.
Reference: The above write-up on Integers and Floating Point Numbers has been referred from the excellent eBook “An Introduction to Programming in Go19” by Caleb Doxsey.
Later on do read up on the big package.
2.9.3 Some default values
| Type | Initialized Value |
|---|---|
| Boolean | false |
| Integer | 0 |
| Floating Point | 0.0 |
| String |
"" (empty string) |
Note: When memory is allocated to store a value, either through a declaration or a call of make or new, and no explicit initialization is provided, the memory is given a default initialization. Each element of such a value is set to the zero value for its type: false for booleans, 0 for integers, 0.0 for floats, “” for strings, and nil for pointers, functions, interfaces, slices, channels, and maps.
2.10 Console output
2.10.1 Print and Println
The function Print print formats using the default formats for its operands and writes to standard output. Spaces are added between operands when neither is a string.
The function Println formats using the default formats for its operands and writes to standard output. Spaces are always added between operands and a newline is appended.
If you have Go installed on your local machine, then you can read the documentation of any of these functions. Example:
godoc fmt Println
2.10.2 Formatting using Printf
Function Printf from the package fmt use the following format verbs:
| General | %v | the value in a default format |
| Boolean | %t | the word true or false |
| Integer | %d | base 10 |
| Floating-point and complex | %f | decimal point but no exponent, e.g. 123.456 |
| %e | scientific notation, e.g. -1234.456e+78 | |
| %g | %e for large exponents, %f otherwise | |
| String | %s | string |
The default value for %v is:
| bool | %t |
| int, int8 etc. | %d |
| uint, uint8 etc. | %d |
| float32, complex64, etc. | %g |
| string | %s |
Please refer to fmt.Printf format reference - cheat sheet20
Let’s look at an example.
1 package main
2
3 import "fmt"
4
5 func main() {
6 // Formatting booleans
7 fmt.Printf("%v\n", false)
8 fmt.Printf("%t\n", true)
9
10 // Formatting integers
11 fmt.Printf("%v\n", 987)
12 fmt.Printf("%d\n", 345)
13
14 // Formatting floats
15 fmt.Printf("%v\n", 987123456.987345123432)
16 fmt.Printf("%f\n", 987123456.987345123432)
17 // Use %g for large exponents
18 fmt.Printf("%g\n", 987123456.987345123432)
19
20 // When formatting numbers you will often
21 // want to control the width and precision
22 // of the resulting figure. To specify the
23 // width of an integer, use a number after
24 // the % in the verb. By default the result
25 // will be right-justified and padded with
26 // spaces.
27 fmt.Printf("|%6d|%6d|\n", 23, 987)
28
29 // You can also specify the width of printed
30 // floats, though usually you'll also want to
31 // restrict the decimal precision at the same
32 // time with the width.precision syntax.
33 fmt.Printf("|%6.2f|%6.2f|\n", 3.2, 4.37)
34
35 // To left-justify, use the - flag.
36 fmt.Printf("|%-6.2f|%-6.2f|\n", 3.2, 4.37)
37
38 // So far we’ve seen Printf, which prints
39 // the formatted string to os.Stdout.
40 // Sprintf formats and returns a string
41 // without printing it anywhere.
42 s := fmt.Sprintf("a %s", "string")
43 fmt.Println(s)
44
45 fmt.Println(2001,3456)
46 fmt.Print(20,30)
47 }
Once the Go Playground loads up, clear whatever is written on the screen and type out the above program.
Click on the Run button and check the output at the bottom of the screen.
false
true
987
345
9.871234569873451e+08
987123456.987345
9.871234569873451e+08
| 23| 987|
| 3.20| 4.37|
|3.20 |4.37 |
a string
2001 3456
20 30
2.11 Console input
The functions Scan, Scanf and Scanln from the fmt package read from os.Stdin.
Scan scans text read from standard input, storing successive space-separated values into successive arguments. Newlines count as space. It returns the number of items successfully scanned.
Scanf scans text read from standard input, storing successive space-separated values into successive arguments as determined by the format. It returns the number of items successfully scanned. Scanf parses the arguments according to a format string, analogous to that of Printf with certain restrictions.
Scanln is similar to Scan, but stops scanning at a newline and after the final item there must be a newline or EOF.
Write the following program and store it in the same folder where you had stored hello.go:
1 package main
2
3 import "fmt"
4
5 func main() {
6 var username string
7
8 fmt.Println("Enter Username: ")
9 fmt.Scanf("%s", &username)
10
11 fmt.Println("Username:", username)
12 }
For Windows users, use fmt.Scanf("%s\n", &username) instead of fmt.Scanf("%s", &username).
Run the program.
We shall learn about var and the & operator in later chapters.
2.12 Command-line arguments - Args
When we invoke a command on the terminal it’s possible to pass the command arguments.
In the command go run hello.go, run and hello.go are arguments to the go command.
In the package os we have a variable Args: var Args []string
Args hold the command-line arguments, starting with the program name. os.Args provides access to raw command-line arguments. Note that the first value in this slice is the path to the program, and os.Args[1:] holds the arguments to the program.
Note: We talk about slices in a later chapter.
Write the program cmd_line_args.go as follows and store it in the same folder where you had stored hello.go:
1 package main
2
3 import "fmt"
4 import "os"
5
6 func main() {
7 //You can get individual args with normal indexing
8 fmt.Println(os.Args[1], os.Args[2], os.Args[3], os.Args[4])
9 }
Run this program, as follows:
go run cmd_line_args.go a1 a2 a3 a4
The output you will see is something like this:
a1 a2 a3 a4
2.13 Solutions
- https://play.golang.org/↩
- http://play.golang.org/p/sOqjtiXkRF↩
- https://play.golang.org/↩
- http://golang.org/ref/spec#Bootstrapping↩
- http://golang.org/ref/spec#Keywords↩
- http://golang.org/pkg/fmt/↩
- http://golang.org/pkg/↩
- https://godoc.org/golang.org/x/tools/cmd/goimports↩
- http://golang.org/doc/faq#unused_variables_and_imports↩
- https://github.com/golang/go/wiki/CodeReviewComments#imports↩
- https://golang.org/doc/go1compat↩
- https://golang.org/doc/go1compat↩
- http://play.golang.org/p/9vq29NXfno↩
- https://golang.org/pkg/math/↩
- https://golang.org/pkg/math/#Inf↩
- https://golang.org/pkg/math/#NaN↩
- https://golang.org/pkg/math/#IsInf↩
- https://golang.org/pkg/math/#IsNaN↩
- https://www.golang-book.com/books/intro/3↩
- http://golang-examples.tumblr.com/post/86795367134/fmt-printf-format-reference-cheat-sheet↩
- http://play.golang.org/p/7Ofcns7kWR↩
3. Variables, Constants, and Types
Once the Go Playground loads up, clear whatever is written on the screen and type:
1 package main
2
3 import "fmt"
4
5 var x int
6
7 func main() {
8 p := 5
9 var q int
10 fmt.Println("p is: ", p)
11 fmt.Println("q is: ", q)
12 fmt.Println("x is: ", x)
13 }
When we run the program, the output is:
p is: 5
q is: 0
x is: 0
3.1 Variable
A variable is a storage location for holding a value. The set of permissible values is determined by the variable’s type.
The var statement declares a list of variables.
A var statement can be at package or function level as seen in the above program.
Go can infer the type of the declared type. For integer literals Go infers type int, for floating-point literals Go infers type float64, and for complex literals Go infers type complex128 (the numbers in their names refer to how many bits they occupy). The normal practice is to leave types unspecified unless we want to use a specific type that Go won’t infer.
In the above program we have p := 5 which means that if an initializer is present, the type can be omitted; the variable will take the type of the initializer. Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type. Outside a function the := construct is not available.
Here Go automatically allocates some memory for the variable p - the size of the memory allocated here is that required by a single integer. Since we have also said p := 5, the integer value 5 is assigned to that memory space after the space is allocated. For variable q, no assignment has been stated. However, Go assigns a “zero-value” by default to most data types. For numeric fields, the value is 0.
Also note that p := 5 is the same as var p int = 5. Observe that in Go we declare the type1 after the variable name as in var q int. int is either 32 or 64 bits.
Multiple var declarations may also be grouped; const and import also allow this. Note the use of parentheses:
1 var (
2 x int
3 b bool
4 )
Multiple variables of the same type can also be declared on a single line:
var x, y int
makes x and y both int variables.
You can also make use of parallel assignment:
a, b := 35, 9
Which makes a and b both integer variables and assigns 35 to a and 9 to b.
Declared, but otherwise, unused variables are a compiler error in Go. The following code:
1 package main
2
3 func main() {
4 var p int
5 }
generates this error:
p declared and not used
Note: A list of common mistakes you can avoid is available3.
Go is statically typed. Every variable has a static type, that is, exactly one type known and fixed at compile time: int, float32, *MyType, []byte, and so on. If we declare:
1 type MyInt int
2
3 var i int
4 var j MyInt
then i has type int and j has type MyInt. The variables i and j have distinct static types and, although they have the same underlying type, they cannot be assigned to one another without a conversion.
Reference: The above has been mentioned in the Go Blog: The Laws of Reflection4
3.2 Constants
Constants are declared like variables, but with the const keyword. Constants can be character, string, boolean, or numeric values. Constants cannot be declared using the := syntax. Numeric constants are high-precision values.
The convention in Go for constants is to use MixedCaps or mixedCaps rather than underscores or all CAPS, to write multiword constant names.
1 const secretKey = 23232390 // Good
2 const SecretKey = 23232390 // Good
3 const secret_key = 23232390 // Bad
4 const SECRETKEY =23232390 // Bad
3.2.1 Typed and Untyped constants
1 // Untyped Constants.
2 const ui = 12345 // kind: integer
3 const uf = 3.141592 // kind: floating-point
4 const hi = "Hello" // kind: string
5
6 // Typed Constants
7 const ti int = 12345 // kind: integer with int64 precision restrictions.
8 const tf float64 = 3.141592 // kind: floating-point with float64 precision restrictions.
In the code snippet above, hi is an untyped string constant. An untyped constant is just a value, one not yet given a defined type that would force it to obey the strict rules that prevent combining differently typed values.
Rob Pike’s article5 has a detailed coverage of untyped and typed constants.
3.3 Named types
Named types are another way to create user defined types. They provide some interesting feature/functionality that is not always available in other languages. The standard library in Go used named types very effectively.
Two examples:
Clear the Go Playground screen of whatever is written there and type:
1 // Sample program to show how to declare and use a named type.
2 package main
3
4 import "fmt"
5
6 // Duration is a named type that represents a duration
7 // of time in Nanosecond.
8 type Duration int64
9
10 func main() {
11 // Declare a variable of type Duration
12 var duration Duration
13 fmt.Println(duration)
14
15 // Declare a variable of type int64 and assign a value.
16 nanosecond := int64(10)
17
18 // Attemped to assign a variable of type
19 // int64 (base type of Duration) to
20 // a variable of type Duration.
21 duration = nanosecond
22
23 // error: cannot use nanosecond (type int64) as type
24 // Duration in assignment
25 }
When we run the program, the output is:
main.go:21: cannot use nanosecond (type int64) as type Duration in assignment
Clear the Go Playground screen of whatever is written there and type:
1 // Sample program to show how to declare and use a named type.
2 package main
3
4 import "fmt"
5
6 // Duration is a named type that represents a duration
7 // of time in Nanosecond.
8 type Duration int64
9
10 func main() {
11 // Declare a variable of type Duration
12 var duration Duration
13 fmt.Println(duration)
14
15 // Declare a variable of type int64 and assign a value.
16 nanosecond := int64(10)
17
18 // Attemped to assign a variable of type
19 // int64 (base type of Duration) to
20 // a variable of type Duration.
21 // duration = nanosecond
22
23 // error: cannot use nanosecond (type int64) as type
24 // Duration in assignment
25
26 // Convert a value of type int64 to type Duration.
27 duration = Duration(nanosecond)
28 fmt.Println(duration)
29 }
When we run the program, the output is:
0
10
3.4 Boolean types
A boolean type represents the set of boolean truth values denoted by the predeclared constants true and false. The boolean type is bool.
3.5 Operators
Go supports the normal set of numerical operators. The table below lists the current ones and their relative precedence. They all associate from left to right.
| Precedence | Operator(s) |
|---|---|
| Highest | * / % << >> & &^ |
+ - | ^ |
|
| == != < <= > >= | |
| <- | |
| && | |
| Lowest | || |
Although Go does not support operator overloading (or method overloading for that matter), some of the built-in operators are overloaded. For instance, + can be used for integers, floats, complex numbers and strings (adding strings is concatenating them).
3.5.1 Boolean Operators
| Syntax | Description/Result |
|---|---|
| !b | Logical not operator; false if Boolean expression b is true |
a || b |
Short-circuit logical OR operator; true if either Boolean |
| expression a or b is true | |
| a && b | Short-circuit logical AND operator; true if both Boolean |
| expressions a and b are true | |
| x < y | true if expression x is less than expression y |
| x <= y | true if expression x is less than or equal to expression y |
| x == y | true if expression x is equal to expression y |
| x != y | true if expression x is not equal toexpression y |
| x >= y | true if expression x is greater than or equal to expression y |
| x > y | true if expression x is greater than expression y |
3.5.2 Arithmetic Operators
| Syntax | Description/Result |
|---|---|
| +x | x |
| -x | The negation of x |
| x++ | Increments x by the untyped constant 1 |
x-- |
Decrements x by the untyped constant 1 |
| x += y | Increments x by y |
| x -= y | Decrements x by y |
| x *= y | Sets x to x multiplied by y |
| x /= y | Sets x to x divieded by y; if the numbers are integers any |
| remainder is discarded; division by zero causes a runtime panic | |
| x + y | The sum of x and y |
| x - y | The result of subtracting y and x |
| x * y | The result of multiplying y and x |
| x / y | The result of dividing y and x; if the numbers are integers any |
| remainder is discarded; division by zero causes a runtime panic |
Although Go has sensible rules of precedence, it is recommended to use parentheses to make intentions clear. Using parentheses is particularly recommended for programmers who use multiple languages so as to avoid subtle mistakes.
3.5.3 Bitwise Operators
There are many good online tutorials on Bitwise operators and we shall not be talking about the same. Here’s one6.
Just remember that many languages use ~ as the unary NOT operator (i.e. bitwise complement), but Go reuses the XOR operator (^) for that. Go also has a special ‘AND NOT’ bitwise operator (&^).
3.6 Solutions
- http://golang.org/ref/spec#Types↩
- https://github.com/golang/go/wiki/CodeReviewComments#variable-names↩
- https://github.com/golang/go/wiki/CodeReviewComments↩
- http://blog.golang.org/laws-of-reflection↩
- https://blog.golang.org/constants↩
- http://code.tutsplus.com/articles/understanding-bitwise-operators–active-11301↩
- http://play.golang.org/p/LtFP2Gkpdi↩
- http://play.golang.org/p/-3EAKb4ZGe↩
- http://play.golang.org/p/NZ1N721NbY↩
- http://play.golang.org/p/pol2hdWsTy↩
4. Control Structures
4.1 if-else
1 package main
2
3 import "fmt"
4
5 func main() {
6 x, y := 30, 45
7 if x < y {
8 fmt.Println("x is less than y")
9 } else if x > y {
10 fmt.Println("x is greater than y")
11 } else {
12 fmt.Println("x is equal to y")
13 }
14 }
Run the above program and the output that you will see is:
x is less than y
Note:
- There may be zero or more
else ifclauses and zero or one finalelseclause. Each block consists of zero or more statements. - The expression after the
ifand theelse ifhas to evaluate to one of thebooltypes:trueorfalse. - 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 elsekeywords to be on the same line with the corresponding braces. If not you will see errors. - You can have the explicit values like
trueorfalse, or you can also use any expression that evaluates to atrueorfalse. - Note that types like an
intcannot be used as truth values - this is unlike languages like C, where you can use integers and pointers as truth values.
4.2 goto statement
Go has a goto statement, which you should use wisely. With goto you jump to a label which must be defined within the current function. For instance:
1 package main
2
3 import "fmt"
4
5 func main() {
6 i := 0
7 Label: // First word on a line ending with a colon is a label
8 fmt.Println(i)
9 i++
10 if i > 50 {
11 goto Escape
12 }
13
14 goto Label // Jump
15 Escape: // Label
16 fmt.Println("Get out of here...")
17 }
The name of the label is case sensitive.
4.3 switch
The general format of the switch is:
1 switch any expression of type T {
2 case any expression of same type T: { code to be executed if the case expression is equal \
3 to the switch expression }
4 case any expression of same type T: { code to be executed if this case expression is equal\
5 to the switch expression }
6 //any number of case statements are allowed
7 default: { code to be executed if none of the case expressions match the switch expression\
8 }
9 }
Points to note:
- the type of the evaluated value in the
switchand thecaseshould match. For example, if theswitchexpression evaluates to anintand 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 totrueis executed, and none of the others are executed - if more than one
casestatements match, then the first in the lexical order is executed - unlike certain other languages, each
caseblock is independent and the code does not “fall through” (each of thecasecode blocks are like independentif-elsecode blocks.) - the
defaultcode block is executed if none of the othercaseblocks match - the
caseexpressions need not be just constants and can have expressions - the use of curly braces for demarcating the code blocks is optional
- the
defaultstatement is optional - any number of
casestatements are allowed - even zerocasestatements are valid, though that wouldn’t be very useful - multiple expressions can be used, each separated by commas in the
casestatement - the
defaultblock can be anywhere within theswitchblock, and not necessarily last in the lexical order
Please refer to Control structures - Go switch case statement1 for more information.
1 package main
2
3 import "fmt"
4
5 func main() {
6 switch {
7 default:
8 fmt.Println("I am default")
9 case false:
10 fmt.Println("I am false")
11 case true:
12 fmt.Println("I am true")
13 }
14 }
1 package main
2
3 import "fmt"
4
5 func main() {
6 i := 5
7 switch i {
8 case 4:
9 fmt.Println("number 4")
10 case i > 8:
11 fmt.Println("i is > 8")
12 default:
13 fmt.Println("default")
14 }
15 }
4.3.1 fallthrough statement
In certain languages like C and Java, the switch-case statement behaves slightly differently from Go: when a case block is executed, all the case blocks below it are also executed, unless explicitly terminated (by say using a break statement). In Go, this is not the default behavior, but if you want to achieve the same result, then use the fallthrough statement to indicate that the case block following the current one has to be executed.
1 k := 3
2 switch k {
3 case 3: fmt.Println("was <= 3"); fallthrough;
4 case 4: fmt.Println("was <= 4"); fallthrough;
5 ...
4.4 for loop
Go has only one looping construct, the for loop.
The basic for loop looks as it does in C or Java, except that the ( ) are gone (they are not even optional) and the { } are required.
There are three forms of for, only one of which has semicolons.
1 // Like a C for
2 for init; condition; post { }
3
4 // Like a C while
5 for condition { }
6
7 // Like a C for(;;)
8 for {
9 }
Here’s an example:
1 package main
2
3 import "fmt"
4
5 func main() {
6 sum := 0
7 for i := 0; i < 100; i++ {
8 sum += i
9 }
10 fmt.Println(sum)
11 }
The output of the program is 4950.
The above program is available here2.
Here are some code snippets and their explanation:
1 func main() {
2 sum := 1
3 // As in C or Java, you can leave the
4 // pre and post statements empty.
5 for ; sum < 100; {
6 sum += sum
7 }
8 fmt.Println(sum)
9 }
1 func main() {
2 sum := 1
3 // You can drop the semicolons
4 // for behaves like C's while
5 for sum < 1000 {
6 sum += sum
7 }
8 fmt.Println(sum)
9 }
1 func main() {
2 // infinite loop
3 for {
4 }
5 }
The break keyword allows you to terminate a loop at a point and continue execution at the statement following the end of the for loop block.
An example:
1 package main
2
3 import "fmt"
4
5 func main() {
6 i := 0
7 for {
8 if i >= 10 {
9 break
10 }
11 fmt.Println("Value of i is:", i)
12 i++
13 }
14 fmt.Println("A statement just after the for loop.")
15 }
With the continue keyword you begin the next iteration of the loop, skipping any remaining code. In the same way as break, continue also accepts a label.
The following loop prints 0 to 5.
1 package main
2
3 import "fmt"
4
5 func main() {
6 for i := 0; i < 10; i++ {
7 if i > 5 {
8 // Skip the rest of the remaining code in the loop
9 continue
10 }
11 fmt.Println(i)
12 }
13 }
A nested for loop example:
1 package main
2
3 import "fmt"
4
5 func main() {
6 L:
7 for {
8 for {
9 break L
10 }
11 }
12 fmt.Println("Break out of nested loops")
13 }
O
OO
OOO
OOOO
OOOOO
OOOOOO
OOOOOOO
OOOOOOOO
OOOOOOOOO
OOOOOOOOOO
4.5 Solutions
Exercise 6 Reason: If there is no expression at all in the switch, then by default it is bool. The block that evaluates to true is executed, and none of the others are executed.
Exercise 7 Reason: The program will not compile as the type int and bool don’t match between the case and the switch.
Exercise 9 Solutions. Part a4 and Part b5.
5. Pointers
According to Wikipedia1: A pointer references a location in memory, and obtaining the value stored at that location is known as de-referencing the pointer. As an analogy, a page number in a book’s index could be considered a pointer to the corresponding page; de-referencing such a pointer would be done by flipping to the page with the given page number.
When there is a value stored in memory, it is stored in a physical location. This location is called its address. Many programming languages, including Go, allows you to access the data in a location by specifying its location in memory.
Unlike C, Go has no pointer arithmetic. Do read “Why is there no pointer arithmetic?”2
You obtain the address of a variable by using the & symbol before the variable name. Let’s look at a simple example:
1 package main
2
3 import "fmt"
4
5 func main() {
6 i := 10
7 fmt.Println("The value of i is : ", i)
8 fmt.Println("The address of i is: ", &i)
9 }
The output is:
The value of i is : 10
The address of i is: 0x1043617c
The actual value of the address will differ from machine to machine and even on different executions of the same program as each machine could have a different memory layout and also the location where it is allocated could be different.
Normal programs do not use the numeric value of the address for anything. Instead, what they usually use is the value referenced by the address. You can get the value at an address by using the * symbol before the address.
Let’s look at another example:
1 package main
2
3 import "fmt"
4
5 func main() {
6 v := 50
7
8 fmt.Println("The value of v is : ", v)
9 fmt.Println("The address of v is : ", &v)
10 fmt.Println("The value at address ", &v, " is : ", *(&v))
11 }
The output is:
The value of v is : 50
The address of v is : 0x113a21e0
The value at address 0x113a21e0 is : 50
When a variable holds an address, it is called a pointer. Therefore in the example:
ptr := &v
ptr is a pointer and it holds the address the of v. Putting it differently, ptr is a pointer to the variable v. You could also say that ptr is a reference to the variable v.
Let’s look at an example:
1 package main
2
3 import "fmt"
4
5 func main() {
6 i, j := 20, 5000
7
8 p := &i // point to i
9 fmt.Println(*p) // read i through the pointer
10 *p = 25 // set i through the pointer
11 fmt.Println(i) // see the new value of i
12
13 p = &j // point to j
14 *p = *p / 10 // divide j through the pointer
15 fmt.Println(j) // see the new value of j
16 }
The output is:
20
25
500
Few more examples:
1 package main
2
3 import "fmt"
4
5 func main() {
6 a := 5
7 var ptr *int
8
9 ptr = &a
10
11 fmt.Println(ptr)
12 }
The output on my machine is:
0x1043617c
Recollect that the built-in function new allocates memory. new can be applied to types like int, string etc.
1 package main
2
3 import "fmt"
4
5 func main() {
6 // What new will return back to you is a pointer
7 // to a location in memory set aside that can
8 // hold the type you specified.
9 x := new(string) // Here string is a type
10
11 // A benefit of using new is you can simply
12 // use the variable (pointer) without having to
13 // use the & operator
14 *x = "Hello"
15 fmt.Println(*x)
16 }
The output is:
Hello
1 package main
2
3 import "fmt"
4
5 func main() {
6 v := new(int)
7 *v++
8 fmt.Println(*v)
9 }
The output is:
1
1 package main
2
3 func main() {
4 const i = 5
5 ptr1 := &i
6 ptr2 := &10
7 }
1 package main
2
3 func main() {
4 var p *int // Declare a nil value pointer
5 *p = 10
6 }
5.1 Solutions
Exercise 11 Reason: You can only take the pointer to a variable and not to a literal value or a constant.
Exercise 12 Reason: If our code attempts to read or write to address 0x0, the hardware will throw an exception that will be caught by the Go runtime and reported back up to our program in the form of a panic. If the panic is not recovered, a stack trace is produced.
6. More on Functions
Functions are reusable units of code. Considering the black-box analogy, a function is a black unit of code to the outside, which is stimulated by some well known inputs (arguments or parameters) to do some work and then produce an outcome (either an output or a result). Functions are declared / defined once and used many times.
func (p mytype) funcname(q int) (r,s int) { return 0,0 }
- The keyword
funcis used to declare a function. - A function can optionally be bound to a specific type. This is called the receiver.
-
funcnameis the name of your function. - The variable q of type
intis 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 thatreturnis a statement so the braces around the parameter(s) are optional. - Functions can be declared, in a program, in any order you wish. The compiler scans the entire file before execution, so function prototyping is a thing of the past in Go.
- Function names starting with a capital letter are exported outside the package, that is, they are visible and can be used by other packages; then they follow PascalCasing eg. MyFunctionInGo().
- When a function is not exported outside a package; then they follow camelCasing: every new word in the name starts with a capital letter eg. myFunctionInGo().
- Functions are values. Functions can be passed around just like any other value. A function’s type signature describes the types of its arguments and return values.
Examples:
Here are two examples. The first is a function without a return value, while the next one is a simple function that returns its input.
func subroutine(in int ) { return }
func identity(in int ) int { return in }
6.1 A function can have multiple return values. Some examples:
1 x, err := f()
Here is another example:
1 func f() (int, int) {
2 return 5, 6
3 }
4
5 func main() {
6 x, y := f()
7 }
Note: Three changes are necessary: change the return type to contain multiple types separated by comma, change the expression after the return so that it contains multiple expressions separated by comma and finally change the assignment statement so that multiple values are on the left side of the := or =.
It is legitimate to discard some or all of a function’s return values by assigning them to the blank identifier (_). However, if no return values are wanted it is more conventional to simply ignore them.
Here’s an example:
1 package main
2
3 import "fmt"
4
5 func multipleVals()(string, int){
6 return "Go-Lang",100
7 }
8
9 func main() {
10 lang, number := multipleVals()
11 fmt.Println(lang)
12 fmt.Println(number)
13
14 // Ignore the first return value with Blank identifier
15 _, anotherNum := multipleVals()
16 fmt.Println(anotherNum)
17 }
The output is:
Go-Lang
100
100
6.2 A function can take zero or more arguments
1 func add(x int, y int) int {
2 return x + y
3 }
When two or more consecutive named function parameters share a type, you can omit the type from all but the last.
1 package main
2
3 import "fmt"
4
5 func add(i, j int) int {
6 return i + j
7 }
8
9 func main() {
10 fmt.Println(add(42, 13))
11 }
In the above example, we shortened i int, j int to i, j int.
This section is referred from A Tour of Go.1
6.3 Named return values
Go’s return values may be named and act just like variables. These names should be used to document the meaning of the return values.
A return statement without arguments returns the current values of the results. This is known as a “naked” return. Naked return statements should be used only in short functions, as with the example shown here. They can harm readability in longer functions.
1 package main
2
3 import "fmt"
4
5 func split(sum int) (x, y int) {
6 x = sum * 4 / 9
7 y = sum - x
8 return
9 }
10
11 func main() {
12 fmt.Println(split(17))
13 }
This section is referred from A Tour of Go.2
6.4 What’s the idiomatic way to exit a program with some error code?
func Exit(code int)
Exit causes the current program to exit with the given status code. Conventionally, code zero indicates success, non-zero an error. The program terminates immediately; deferred functions are not run.
You can do something along these lines in most of your real main packages, so that the return err convention is adopted as soon as possible, and has a proper termination:
1 // part code
2 func main() {
3 if err := run(); err != nil {
4 fmt.Fprintf(os.Stderr, "error: %v\n", err)
5 os.Exit(1)
6 }
7 }
8
9 func run() error {
10 err := something()
11 if err != nil {
12 return err
13 }
14 // etc
15 }
1 package main
2
3 import "fmt"
4
5 var i = 10
6
7 func main() {
8 a()
9 b()
10 a()
11 }
12
13 func a() {
14 fmt.Println(i)
15 }
16
17 func b() {
18 i := 20
19 fmt.Println(i)
20 }
The output when you run the above program is:
10
20
10
1 package main
2
3 import "fmt"
4
5 var i int
6
7 func main() {
8 i := 20
9 fmt.Println(i)
10 b()
11 }
12
13 func b() {
14 i := 40
15 fmt.Println(i)
16 a()
17 }
18
19 func a() {
20 fmt.Println(i)
21 }
The output when you run the above program is:
20
40
0
6.5 Variadic Functions
We have discussed this along with range in “Variadic Functions using range” in a later chapter.
6.6 Closures
The material on Closures and Recursion has been adapted from An Introduction to Programming in Go.3
It is possible to create functions inside of functions:
1 package main
2
3 import "fmt"
4
5 func main() {
6 x := 0
7 increment := func() int {
8 x++
9 return x
10 }
11 fmt.Println(increment())
12 fmt.Println(increment())
13 }
The output is:
1
2
increment is a local variable that has the type func() int (a function that takes no parameters and returns an int).
increment adds 1 to the variable x which is defined in the main function’s scope. This x variable can be accessed and modified by the increment function. This is why the first time we call increment we see 1 displayed, but the second time we call it we see 2 displayed.
A function like this together with the non-local variables it references is known as a closure. In this case increment and the variable x form the closure.
One way to use closure is by writing a function which returns another function which – when called – can generate a sequence of numbers. For example here’s how we might generate odd numbers:
1 package main
2
3 import "fmt"
4
5 //This function makeOddGenerator returns
6 //another function, which we define anonymously
7 //in the body of makeOddGenerator
8 func makeOddGenerator() func() int {
9 i := 1
10 return func() int {
11 i += 2
12 return i
13 }
14 }
15
16 func main() {
17 nextOdd := makeOddGenerator()
18 fmt.Println(nextOdd())
19 fmt.Println(nextOdd())
20 fmt.Println(nextOdd())
21
22 //To confirm that the state is unique
23 //to that particular function, create
24 //and test a new one
25 newOdd := makeOddGenerator()
26 fmt.Println(newOdd())
27 fmt.Println(newOdd())
28 fmt.Println(newOdd())
29 }
The output is:
3
5
7
3
5
7
Closures are syntactically lighter than a named function. So, if there’s a scenario where a function is used only once or a limited number of times it’s better to go for Closures.
6.7 Recursion
Go supports recursive functions i.e. a function is able to call itself. Here’s a classic factorial example:
1 package main
2
3 import "fmt"
4
5 func fact(n int) int {
6 if n == 0 {
7 return 1
8 }
9 return n * fact(n-1)
10 }
11 func main() {
12 fmt.Println(fact(8))
13 }
Closure and recursion are powerful programming techniques which form the basis of a paradigm known as functional programming. Most people will find functional programming more difficult to understand than an approach based on for loops, if statements, variables and simple functions.
6.8 Solutions
Exercise 13 Reason: Variables declared outside any functions are global in Go, those defined in functions are local to those functions. If names overlap — a local variable is declared with the same name as a global one — the local variable hides the global one when the current function is executed.
Exercise 14 Reason: A local variable is only valid when we are executing the function in which it is defined.
YES…BUT THERE’S MORE…
7. Detailed information
7.1 Semicolons
Like C, Go’s formal grammar uses semicolons to terminate statements1, but unlike in C, those semicolons do not appear in the source.
Idiomatic Go programs have semicolons only in places such as for loop clauses, to separate the initializer, condition, and continuation elements. They are also necessary to separate multiple statements on a line, should you write code that way.
One consequence of the semicolon insertion rules is that you cannot put the opening brace of a control structure (if, for, switch, or select) on the next line. If you do, a semicolon will be inserted before the brace, which could cause unwanted effects. Write them like this:
if i < f() {
g()
}
not like this:
7.2 init
Every Go package may contain one or more init() functions specifying actions that should be taken during program initialisation. Here, multiple declarations of the same identifier can occur without either resulting in a compilation error or the shadowing of a variable.
1 package main
2
3 import "fmt"
4
5 const Hello = "Hi!"
6 var world string
7
8 func init() {
9 world = "My world."
10 }
11
12 func main() {
13 fmt.Println(Hello, world)
14 }
The output is:
Hi! My world.
7.3 Formatting
A Go program can be formatted in (almost) any way the programmers want. With Go, let the machine take care of most formatting issues. The gofmt program2 (also available as go fmt, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments.
So if your program name is hello.go then format it as follows:
go fmt hello.go
The output of the filter gofmt is the officially endorsed format.
The cool thing about Go code is that the officially endorsed format includes tabs, and so if you like having your tabs present as the traditional 8 spaces, go ahead. If you want them to present as 2 spaces, go ahead, if you want them to present as 4 spaces, or 20 spaces, you are not affecting anyone else with your preferences.
7.4 More on Strings
Strings3 are a sequence of UTF-8 characters (one-byte UTF-8 codes have the same value as the corresponding ASCII code). UTF-8 is the most widely used encoding, the standard encoding for text files, XML files and JSON strings. While able to represent characters that need 4 bytes, ASCII-characters are still stored using only 1 byte. A Go string is thus a sequence of variable-width characters (each 1 to 4 bytes), contrary to strings in other languages as C++, Java or Python that are fixed-width (Java uses always 2 bytes).
The advantages are that Go strings and text files occupy less memory/disk space, and since UTF-8 is the standard, Go does not need to encode and decode strings as other languages have to do. Strings are value types and immutable: once created you cannot modify the contents of the string.
A Go string is a sequence of variable-width characters where each character is represented by one or more bytes, normally using the UTF-8 encoding. Strings are length-delimited and not terminated by a special character as in C/C++.
The initial (default) value of a string is the empty string "".
String literals have type string. String literals are of two types:
- Interpreted strings i.e. surrounded by
""(double quotes). - Raw strings i.e. surrounded by back quotes.
They are not interpreted; they can span multiple lines. In the literal `This is a raw string \n` the \n is not interpreted but taken literally.
7.4.1 String functions
The standard library’s strings4 package provides many useful string-related functions. Here are a few in action:
1 package main
2
3 import "fmt"
4 import s "strings"
5
6 // We alias fmt.Println to a shorter name
7 var p = fmt.Println
8
9 func main() {
10 // Note that these are all functions from package, not methods on the
11 // string object itself. This means that we need pass the string in
12 // question as the first argument to the function.
13 p("ToLower: ", s.ToLower("TEST"))
14 p("ToUpper: ", s.ToUpper("test"))
15 p("Split: ", s.Split("a-b-c-d-e", "-"))
16 p("Index: ", s.Index("test", "e"))
17 p("Contains: ", s.Contains("test", "es"))
18 p("Count: ", s.Count("test", "t"))
19 }
The output is:
ToLower: test
ToUpper: TEST
Split: [a b c d e]
Index: 1
Contains: true
Count: 2
7.5 defer
Go has a special statement called defer6 which schedules a function call to be run after the function completes.
defer is often used when resources need to be freed in some way. For example when we open a file we need to make sure to close it later. With defer:
1 file, err := os.Open("hello.go")
2 check(err)
3 defer file.Close()
This has 3 advantages:
- it keeps our
Closecall near ourOpencall so its easier to understand, - if our function had multiple return statements (perhaps one in an if and one in an else)
Closewill happen before both of them and - deferred functions are run even if a run-time error occurs.
Another example showing that defer runs when the function returns and not when the scope is exited Also, that defer’ed function calls are run in LIFO order:
1 package main
2
3 import "fmt"
4
5 func f() {
6 for i := 0; i < 5; i++ {
7 defer func(n int) { fmt.Println("bye", n) }(i)
8 fmt.Println(i)
9 }
10 }
11
12 func main() {
13 f()
14 }
The output is:
0
1
2
3
4
bye 4
bye 3
bye 2
bye 1
bye 0
7.6 Using the log package
Usage: import "log"
Package log implements a simple logging package. It defines a type, Logger, with methods for formatting output. It also has a predefined ‘standard’ Logger accessible through helper functions Print[f|ln], Fatal[f|ln], and Panic[f|ln], which are easier to use than creating a Logger manually. That logger writes to standard error and prints the date and time of each logged message. The Fatal functions call os.Exit(1) after writing the log message. The Panic functions call panic after writing the log message.
Example: log.Fatal(err)
7.7 Using the big package
Package big implements multi-precision arithmetic (big numbers). The following numeric types are supported:
-
Intsigned integers -
Ratrational numbers
func NewInt(x int64) *Int
NewInt allocates and returns a new Int set to x.
func (z *Int) Mul(x, y *Int) *Int
Mul sets z to the product x*y and returns z.
Here’s an example:
1 package main
2
3 import (
4 "fmt"
5 "math/big"
6 )
7
8 func main() {
9 verybig := big.NewInt(1)
10 ten := big.NewInt(10)
11
12 for i:=0; i<10000; i++ {
13 temp := new(big.Int)
14 temp.Mul(verybig, ten)
15 verybig = temp
16 }
17
18 fmt.Println(verybig)
19 }
The output is:
1000.....
Note: If you want it to run fast enough for Go Playground, use a smaller exponent than 100000.
7.8 go vet
go vet7 is used for analysing Go code and finding common mistakes. Among many it checks for some common mistakes like:
- Useless assignments
- Comparing functions with nil
- Using wrong printf format specifiers
- Closing over loop variables the wrong way
- Unreachable code
- Mistakes involving boolean operators
7.9 golint
Go is a tool8 for managing Go source code.
Usage: go command [arguments] where get is one of the many commands available.
go get downloads and installs the packages named by the import paths, along with their dependencies.
Install golint with go get github.com/golang/lint/golint
While go vet checks your code for actual programming errors, golint checks your code for style violations.
Golint does not emit errors or warnings, but “suggestions”: These suggestions can be wrong at times, and code that golint complains about isn’t necessarily wrong – it might just be hitting a false positive. Nevertheless, it’s more often right than wrong, and you should definitely run golint on your code from time to time and fix those suggestions that it is right about.
7.10 goimports
Install goimports with go get golang.org/x/tools/cmd/goimports
Do you wish Go would just allow you to import unused packages? Or are you even tired of having to manually import all those packages you use? Then goimports might be for you. It’s a replacement for gofmt that, in addition to formatting your code, it also manages your imports for you.
It removes unused imports and adds missing imports.
Usage: goimports -w go_program.go
7.11 Naming Convention Summary
There is no detailed coding style guide for Golang.
Here are some references that would help:
Between gofmt, golint and go vet, you cover already a lot of conventions.
Do read “Is there a Go programming style guide?”13
- https://golang.org/doc/effective_go.html#semicolons↩
- https://golang.org/doc/effective_go.html#formatting↩
- https://blog.golang.org/strings↩
- http://golang.org/pkg/strings/↩
- https://www.socketloop.com/tutorials/golang-reverse-a-string-with-unicode↩
- https://golang.org/doc/effective_go.html#defer↩
- https://golang.org/cmd/go/#hdr-Run_go_tool_vet_on_packages↩
- https://golang.org/cmd/go/↩
- https://github.com/golang/go/wiki/CodeReviewComments↩
- https://golang.org/doc/code.html↩
- http://blog.golang.org/godoc-documenting-go-code↩
- https://golang.org/doc/effective_go.html↩
- http://golang.org/doc/faq#Is_there_a_Go_programming_style_guide↩
8. Arrays
In Go, an array is a numbered sequence of elements of a specific length.
Arrays are easily understood with the help of this example:
1 package main
2
3 import "fmt"
4
5 func main() {
6 //exactly 5 ints. The type of elements
7 //and length are both part of the array's
8 //type. By default an array is zero-valued,
9 //and int types are by default initialized
10 //with a 0 value.
11 var arr [5]int
12 fmt.Println("arr:", arr)
13
14 //We can set a value at an index using the
15 //array[index] = value syntax, and get a
16 //value with array[index].
17 arr[3] = 10
18 fmt.Println("set:", arr)
19 fmt.Println("get:", arr[3])
20
21 //The builtin len returns the length of
22 //an array
23 fmt.Println("len:", len(arr))
24
25 //Use this syntax to declare and initialize
26 //an array in one line.
27 arr2 := [5]int{1, 2, 3, 4, 5}
28 fmt.Println("arr2:", arr2)
29
30 //multidimensional array
31 arr3 := [2][2] int{ {1,2}, {3,4} }
32 fmt.Println("arr3:", arr3)
33 }
The output is:
arr: [0 0 0 0 0]
set: [0 0 0 10 0]
get: 10
len: 5
arr2: [1 2 3 4 5]
arr3: [[1 2] [3 4]]
Note that you can have the compiler count the array elements for you:
b := [...]string{"Golang", "Challenge"}
In both cases, the type of b is [2]string.
Go’s arrays are values. An array variable denotes the entire array; it is not a pointer to the first array element (as would be the case in C). This means that when you assign or pass around an array value you will make a copy of its contents. (To avoid the copy you could pass a pointer to the array, but then that’s a pointer to an array.)
The in-memory representation of [4]int is just four integer values laid out sequentially:
9. Slices
Arrays are a bit inflexible, so you don’t see them too often in Go code. Slices, though, are everywhere. They build on arrays to provide great power and convenience.
A slice is a segment of an array. Like arrays, slices are indexable and have a length. Unlike arrays, this length is allowed to change. Here’s an example of a slice:
var x []int
The only difference between this and an array is the missing length between the brackets. In this case x has been created with a length of 0.
If you want to create a slice you should use the built-in make function:
x := make([]int, 5)
This creates a slice that is associated with an underlying int array of length 5. Slices are always associated with some array, and although they can never be longer than the array, they can be smaller or equal.
The make function also allows a 3rd parameter:
x := make([]int, 5, 10)
10 represents the capacity of the underlying array which the slice points to. When the capacity argument is omitted, it defaults to the specified length.
The length and capacity of a slice can be inspected using the built-in len and cap functions.
len(x) == 5
cap(x) == 10
Another way to create slices is to use the [low : high] expression:
1 arr := [5]int{1,2,3,4,5}
2 x := arr[0:5]
low is the index of where to start the slice and high is the index where to end it (but not including the index itself). For example while arr[0:5] returns [1,2,3,4,5], arr[1:4] returns [2,3,4].
For convenience we are also allowed to omit low, high or even both low and high. arr[0:] is the same as arr[0:len(arr)], arr[:5] is the same as arr[0:5] and arr[:] is the same as arr[0:len(arr)].
9.1 Slice Functions
Go includes two built-in functions to assist with slices: append and copy. Here is an example of append:
1 package main
2
3 import (
4 "fmt"
5 )
6
7 func main() {
8 slice1 := []int{10, 20, 30}
9 slice2 := append(slice1, 40, 50)
10 fmt.Println(slice1, slice2)
11 }
The output is:
[10 20 30] [10 20 30 40 50]
append creates a new slice by taking an existing slice (the first argument) and appending all the following arguments to it.
Here is an example of copy:
1 package main
2
3 import (
4 "fmt"
5 )
6
7 func main() {
8 slice1 := []int{10, 20, 30}
9 slice2 := make([]int, 2)
10 copy(slice2, slice1)
11 fmt.Println(slice1, slice2)
12 }
The output is:
[10 20 30] [10 20]
After running this program slice1 has [10,20,30] and slice2 has [10,20]. The contents of slice1 are copied into slice2, but since slice2 has room for only two elements only the first two elements of slice1 are copied.
9.2 Slice internals
A slice is a descriptor of an array segment. It consists of a pointer to the array (ptr in image below), the length of the segment (len), and its capacity (cap - the maximum length of the segment).
Now s := make([]int, 5) can be represented as:
The length is the number of elements referred to by the slice. The capacity is the number of elements in the underlying array (beginning at the element referred to by the slice pointer).
As we slice s, observe the changes in the slice data structure and their relation to the underlying array:
s = s[2:4]
Slicing does not copy the slice’s data. It creates a new slice value that points to the original array. This makes slice operations as efficient as manipulating array indices. Therefore, modifying the elements (not the slice itself) of a re-slice modifies the elements of the original slice.
1 package main
2
3 import "fmt"
4
5 func main() {
6 arr := [5]int{1, 2, 3, 4, 5}
7 slice := arr[0:5]
8 fmt.Println(slice)
9 fmt.Println("Len is: ", len(slice))
10 fmt.Println("Cap is: ", cap(slice))
11
12 slice = slice[2:4]
13 fmt.Println(slice)
14 fmt.Println("Len is: ", len(slice))
15 fmt.Println("Cap is: ", cap(slice))
16 }
The output is:
[1 2 3 4 5]
Len is: 5
Cap is: 5
[3 4]
Len is: 2
Cap is: 3
Slicing does not copy the slice’s data. It creates a new slice value that points to the original array. This makes slice operations as efficient as manipulating array indices.
Slices provide a dynamic window onto the underlying array.
9.3 Passing a slice to a function
If you have a function which must operate on an array, create a slice reference and pass that to the function.
For example, here is a function that sums all elements in an array:
1 package main
2
3 import (
4 "fmt"
5 )
6
7 func sum(a []int) int {
8 s := 0
9 for i := 0; i < len(a); i++ {
10 s += a[i]
11 }
12 return s
13 }
14
15 func main() {
16 var arr = [5]int{0, 1, 2, 3, 4}
17 fmt.Println("Sum is: ", sum(arr[:]))
18 }
The output is:
Sum is: 10
10. Range
range iterates over elements in a variety of data structures.
If you’re looping over an array, slice, string, or map (more about this later), or reading from a channel (more about this later), a range clause can manage the loop.
1 package main
2
3 import "fmt"
4
5 func main() {
6 nums := []int{2, 3, 4}
7 sum := 0
8 for key, value := range nums {
9 sum += value
10 fmt.Println("key: ", key, " sum is: ", sum)
11 }
12 fmt.Println("Final sum:", sum)
13 }
The output is:
key: 0 sum is: 2
key: 1 sum is: 5
key: 2 sum is: 9
Final sum: 9
If you only need the first item in the range (the key or index), drop the second:
1 for key := range m {
2 if key.expired() {
3 delete(m, key)
4 }
5 }
If you only need the second item in the range (the value), use the blank identifier, an underscore, to discard the first:
1 package main
2
3 import "fmt"
4
5 func main() {
6 nums := []int{2, 3, 4}
7 sum := 0
8 for _, value := range nums {
9 sum += value
10 }
11 fmt.Println("Final sum:", sum)
12 }
The output is:
Final sum: 9
The blank identifier _ as seen above, can be assigned or declared with any value of any type, with the value discarded harmlessly.
Here is a slighly bigger example:
1 package main
2
3 import "fmt"
4
5 func main() {
6 nums := []int{2, 3, 4}
7
8 //Here we use range to sum the
9 //numbers in a slice. Arrays work
10 //like this too.
11 sum := 0
12 for _, num := range nums {
13 sum += num
14 }
15 fmt.Println("sum:", sum)
16
17 //range on arrays and slices provides
18 //both the index and value for each entry.
19 //Above we didn't need the index, so we
20 //ignored it with the blank identifier _.
21 //Sometimes we actually want the indexes though.
22 for i, num := range nums {
23 if num == 3 {
24 fmt.Println("index:", i)
25 }
26 }
27 }
The output is:
sum: 9
index: 1
You can also use range on strings directly. Then it will break out the individual Unicode characters and their start position, by parsing the UTF-8.
1 package main
2
3 import "fmt"
4
5 func main() {
6 for pos, char := range "a©b" {
7 fmt.Printf("character '%c' starts at byte position %d\n", char, pos)
8 }
9 }
The output is:
character 'a' starts at byte position 0
character `'©'` starts at byte position 1
character 'b' starts at byte position 3
Observe that ‘b’ starts at position 3, which means '©' took 2 bytes.
We had mentioned earlier that in the UTF-8 world characters are sometimes called runes. Mostly, when people talk about characters, they mean 8 bit characters. As UTF-8 characters may be up to 32 bits the word rune is used. In this case the type of char is rune. The Go language defines the word rune as an alias for the type int32.
A for range loop, by contrast, decodes one UTF-8-encoded rune on each iteration. Each time around the loop, the index of the loop is the starting position of the current rune, measured in bytes, and the code point is its value.
10.1 Variadic Functions using range
There is a special form available for the last parameter in a Go function. They are known as Variadic functions and can be called with any number of trailing arguments. By using ... before the type name of the last parameter you can indicate that it takes zero or more of those parameters.
Here’s a function that will take an arbitrary number of ints as arguments:
1 package main
2
3 import "fmt"
4
5 func add(args ...int) int {
6 total := 0
7 for _, v := range args {
8 total += v
9 }
10 return total
11 }
12 func main() {
13 fmt.Println(add(1, 2))
14 fmt.Println(add(4, 5, 6))
15 }
The output is:
3
15
11. Maps
A Go map is a built-in unordered collection of key-value pairs. Also known as an associative array, a hash table or a dictionary, maps are used to look up a value by its associated key.
To create an empty map, use the built-in make:
1 make(map[key-type]val-type)
Note that the key can be an int, a float, complex number, string, pointer, even an interface that supports equality. We’ll look at interfaces later. We can also have a map like map[string]map[string]string. This is a map of strings to maps of strings to strings.
Let us look at the following Go program:
1 package main
2
3 import "fmt"
4
5 func main() {
6 m := make(map[string]int)
7
8 //Set key/value pairs using typical
9 //name[key] = val syntax
10 m["k1"] = 7
11 m["k2"] = 13
12
13 //Printing a map with e.g. Println
14 //will show all of its key/value pairs
15 fmt.Println("map:", m)
16
17 //Get a value for a key with name[key]
18 v1 := m["k1"]
19 fmt.Println("v1: ", v1)
20
21 //The builtin len returns the number of
22 //key/value pairs when called on a map
23 fmt.Println("len:", len(m))
24
25 //The builtin delete removes key/value pairs
26 //from a map
27 delete(m, "k2")
28 fmt.Println("map:", m)
29
30 //The optional second return value when getting
31 //a value from a map indicates if the key was
32 //present in the map. This can be used to
33 //disambiguate between missing keys and keys
34 //with zero values like 0 or ""
35 _, prs := m["k2"]
36 fmt.Println("prs:", prs)
37
38 //You can also declare and initialize a new map
39 //in the same line with this syntax
40 n := map[string]int{"foo": 1, "bar": 2}
41 fmt.Println("map:", n)
42 }
The output of the program is:
map: map[k1:7 k2:13]
v1: 7
len: 2
map: map[k1:7]
prs: false
map: map[foo:1 bar:2]
Note: x := make(map[int]int) looks very much like an array but there are a few differences. First the length of a map (found by doing len(x)) can change as we add new items to it. When first created it has a length of 0, after x[1] = 10 it has a length of 1. Second, maps are not sequential. We have x[1], and with an array that would imply there must be an x[0], but maps don’t have this requirement.
12. Structs
Based on what we have learnt so far, how would you write a program that calculates the area and perimeter of a rectangle?
Here’s what you would do:
1 package main
2
3 import "fmt"
4
5 func Area(length, width float64) float64 {
6 return length * width
7 }
8
9 func Perimeter(length, width float64) float64 {
10 return 2 * (length + width)
11 }
12
13 func main() {
14 length := 4.5
15 width := 3.75
16
17 area := Area(length, width)
18 fmt.Println("Area is = ", area)
19
20 perimeter := Perimeter(length, width)
21 fmt.Println("Perimeter is = ", perimeter)
22 }
The output is:
Area is = 16.875
Perimeter is = 16.5
However, the way we have written the above program is more on the lines of procedural programming1. Let’s explore how to write the above program using Go’s struts but first understanding what structs in Go are.
Go allows you to define new types, it does this with the type keyword:
type foo int
The above creates a new type foo which acts like an int. Creating more sophisticated types is done with the struct keyword.
A struct is a collection of fields and is defined with the type and struct keywords.
An example:
1 type myStructName struct {
2 x int
3 y string
4 }
The type keyword introduces a new type. It is followed by the name of the type (myStructName), the keyword struct to indicate that we are defining a struct type and a list of fields inside of curly braces. Each field has a name and a type. Like with functions we can collapse fields that have the same type:
1 type Circle struct {
2 x, y, r float64
3 }
Struct fields are accessed using a dot. Here’s an example:
1 package main
2
3 import "fmt"
4
5 // this struct is visible only in this package
6 // as it starts with a small letter
7 type myStructName struct {
8 // variables starts with small letter,
9 // so NOT visible outside the package
10 x int
11 y string
12 }
13
14 func main() {
15 msn := myStructName{1, "talim"}
16 msn.x = 4
17 fmt.Println(msn.x)
18 }
The output is is 4.
Let us define a Square struct as follows:
1 package main
2
3 import "fmt"
4
5 // this struct is visible outside this package
6 // as it starts with a capital letter
7 type Square struct {
8 // variable X starts with a capital letter,
9 // so visible outside package
10 X int
11 name string
12 }
13
14 func main() {
15 sqr := Square{}
16 fmt.Println("Default square is: ", sqr)
17 }
When you run the example, the output is Default square is: {0 }
This means that in a struct the values of the variables by default for an int will be 0; a string will be empty, etc.
Now let us look at different ways of initializing a struct and setting values for variables within it.
1 package main
2
3 import "fmt"
4
5 type Square struct {
6 X int
7 name string
8 }
9
10 func main() {
11 //initialize values in order they are defined in struct
12 sqr1 := Square{10, "Go_Sqr1"}
13 fmt.Println("Square sqr1 is : ", sqr1)
14
15 //initialize values by variable name in any order
16 sqr2 := Square{name: "Go_Sqr2", X: 15}
17 fmt.Println("Square sqr2 is : ", sqr2)
18
19 //get pointer to an instance with new keyword
20 ps := new(Square)
21
22 //set value using . notation by dereferencing pointer
23 (*ps).X = 30
24
25 //set value using . same as above
26 ps.name = "Go_Sqr3"
27
28 fmt.Println("Square ps is : ", *ps)
29
30 ps1 := &sqr1
31 ps1.X = 22
32 fmt.Println("The new value of X: ", ps1.X)
33 fmt.Println("Square sqr1 is : ", sqr1)
34 }
The output is:
Square sqr1 is : {10 Go_Sqr1}
Square sqr2 is : {15 Go_Sqr2}
Square ps is : {30 Go_Sqr3}
The new value of X: 22
Square sqr1 is : {22 Go_Sqr1}
Note: As seen above, you can get a pointer to a newly created struct instance using the new keyword. A pointer thus obtained, can be used with or without using the * operator to get variables within it.
Thus new(T) returns a pointer to a newly allocated zero value of type T. This is important to remember.
This means a user of the data structure can create one with new and get right to work.
Another example:
1 package main
2
3 import "fmt"
4
5 type Person struct {
6 name string
7 age int
8 }
9
10 func main() {
11 fmt.Println(Person{"Kiran", 30})
12 fmt.Println(Person{name: "Satish", age: 60})
13
14 s := Person{"Victor", 45}
15 fmt.Println(s)
16
17 r := &Person{"Harry", 30} // Pointer
18 fmt.Println(r)
19 r.age = 33 // Changing the values
20 fmt.Println(*r) // Pointer de-referencing
21
22 // Another example of Pointers
23 p := &Person{}
24 p.name = "Hulk"
25 p.age = 10
26 fmt.Println(*p)
27 }
The output is:
{Kiran 30}
{Satish 60}
{Victor 45}
&{Harry 30}
{Harry 33}
{Hulk 10}
An anonymous struct field
1 package main
2
3 import "fmt"
4
5 type A struct {
6 ax, ay int
7 }
8 type B struct {
9 A
10 bx, by float64
11 }
12
13 func main() {
14 b := B{A{1, 2}, 3.0, 4.0}
15 fmt.Println(b.ax, b.ay, b.bx, b.by)
16 fmt.Println(b.A)
17 }
The output is:
1 2 3 4
{1 2}
Anonymous fields of any type
Any named type, or pointer to one, may be used in an anonymous field and it may appear at any location in the struct.
1 package main
2
3 import "fmt"
4
5 type C struct {
6 x float64
7 int
8 string
9 }
10
11 func main() {
12 c := C{3.5, 7, "hello"}
13 fmt.Println(c.x, c.int, c.string)
14 }
The output is:
3.5 7 hello
Now let us understand how to define a function on a struct.
A normal function that we name myFunc that takes no parameters and returns an int would be defined similar to the code snippet shown below:
1 func myFunc() int {
2 //code
3 }
A function like myFunc that takes no parameters and returns an int, but which is associated with a type which we call as myType would be defined similar to the code snippet shown below:
1 type myType struct{}
2
3 func (f myType) myFunc() int {
4 //code
5 }
In between the keyword func and the name of the function (myFunc()) we have added a “receiver”. The receiver is like a parameter – it has a name and a type. In Go, a function which takes a receiver is usually called a method.
Let’s extend our earlier Square struct to add an Area function. This time we will define that the Area function works explicitly with the Square type:
1 package main
2
3 import "fmt"
4
5 type Square struct {
6 X int
7 name string
8 }
9
10 func (s Square) Area() int {
11 return s.X * s.X
12 }
13
14 func main() {
15 r1 := Square{5, "my_sqr"}
16 fmt.Println("Square is: ", r1)
17 fmt.Println("Square area is: ", r1.Area())
18 }
The output is:
Square is: {5 my_sqr}
Square area is: 25
Let’s now modify the program stuct0.go to use structs.
1 package main
2
3 import "fmt"
4
5 type Rectangle struct {
6 length, width float64
7 }
8
9 func (r Rectangle) Area() float64 {
10 return r.length * r.width
11 }
12
13 func (r Rectangle) Perimeter() float64 {
14 return 2 * (r.length + r.width)
15 }
16
17 func main() {
18 r := Rectangle{4.5, 3.75}
19
20 area := r.Area()
21 fmt.Println("Area is = ", area)
22
23 perimeter := r.Perimeter()
24 fmt.Println("Perimeter is = ", perimeter)
25 }
The output is:
Area is = 16.875
Perimeter is = 16.5
12.1 What’s the difference between pointer and non-pointer method receivers?
We know that in Go, a function which takes a receiver is usually called a method.
func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct) valueMethod() { } // method on value
Simply stated: you can treat the receiver as if it was an argument being passed to the method. All the same reasons why you might want to pass by value or pass by reference apply.
Reasons why you would want to pass by reference as opposed to by value:
- You want to actually modify the receiver (“read/write” as opposed to just “read”)
- The
structis very large and a deep copy is expensive - Consistency: if some of the methods on the
structhave pointer receivers, the rest should too. This allows predictability of behavior
If you need these characteristics on your method call, use a pointer receiver.
Here are even more rules of thumb2 to help you choose whether to use a value or pointer receiver.
1 package main
2
3 import "fmt"
4
5 type Number struct {
6 i int
7 j int
8 }
9
10 func (n Number) Same() {
11 n.i = 23
12 n.j = 47
13 }
14
15 func (n *Number) Change() {
16 n.i = 23
17 n.j = 47
18 }
19
20 func main() {
21 n := &Number{0, 0}
22 fmt.Println(n)
23 n.Same()
24 fmt.Println(n)
25 n.Change()
26 fmt.Println(n)
27 }
The output is:
&{0 0}
&{0 0}
&{23 47}
The method Same is defined with a non-pointer receiver and doesn’t change the values of the struct it is invoked on, and Change is defined with a pointer receiver, so it does change the values of the struct upon which it is invoked.
12.2 Solutions
13. interface
An interface is a set of methods. To turn it around, the methods implemented by a concrete type such as a struct form the interface of that type.
Let’s focus on the method set aspect of interfaces first.
In Go, suppose you have an interface for a Dog:
1 type Dog interface {
2 Woof()
3 }
Any struct that has a Woof method will implement the Dog interface implicitly and can be used as a Dog.
1 package main
2
3 import "fmt"
4
5 type Dog interface {
6 Woof()
7 }
8
9 type Lassie struct {
10 }
11
12 func (l Lassie) Woof() {
13 fmt.Println("woof woof!")
14 }
15
16 type Astro struct {
17 }
18
19 func (a Astro) Woof() {
20 fmt.Println("WOOF WOOF!!")
21 }
22
23 func main() {
24 var dog Dog
25 dog = Lassie{}
26 dog.Woof()
27
28 dog = Astro{}
29 dog.Woof()
30 }
with output:
woof woof!
WOOF WOOF!!
If you have another struct, Cat, which doesn’t have a Woof method and you tried to use it as a Dog then you’ll get a compile time error:
1 package main
2
3 import "fmt"
4
5 type Dog interface {
6 Woof()
7 }
8
9 type Lassie struct {
10 }
11
12 func (l Lassie) Woof() {
13 fmt.Println("woof woof!")
14 }
15
16 type Astro struct {
17 }
18
19 func (a Astro) Woof() {
20 fmt.Println("WOOF WOOF!!")
21 }
22
23 type Cat struct {
24 }
25
26 func (c Cat) Meow() {
27 fmt.Println("meow meow")
28 }
29
30 func main() {
31 var dog Dog
32 dog = Lassie{}
33 dog.Woof()
34
35 dog = Astro{}
36 dog.Woof()
37
38 dog = Cat{}
39 dog.Woof()
40 }
with output:
cannot use Cat literal (type Cat) as type Dog in assignment:
Cat does not implement Dog (missing Woof method)
Earlier we had seen an example of a Square struct and an Area function:
1 package main
2
3 import "fmt"
4
5 type Square struct {
6 Width, Height int
7 }
8
9 func (s Square) Area() int {
10 return s.Width * s.Height
11 }
12
13 func main() {
14 r1 := Square{5, 5}
15 fmt.Println("Square is: ", r1)
16 fmt.Println("Square area is: ", r1.Area())
17 }
with output:
Square is: {5 5}
Square area is: 25
Note: Although not used here, it is idiomatic in Go for interface names to be suffixed with “-er” (like Positioner, Distancer).
Let us now abstract out the Area function into an interface called Geometry. Geometry is an interface and has a single function Area that returns an int as follows:
1 type Geometry interface {
2 Area() int
3 }
Let us write the full program:
1 package main
2
3 import "fmt"
4
5 type Square struct {
6 Width, Height int
7 }
8
9 type Geometry interface {
10 Area() int
11 }
12
13 // To implement an interface in Go, we just need to implement all the methods
14 // in the interface
15 func (s Square) Area() int {
16 return s.Width * s.Height
17 }
18
19 // If a variable has an interface type, then we can call methods that are in
20 // the named interface. Here is a generic measure function taking advantage of
21 // this to work on any Geometry.
22 func measure(g Geometry) {
23 fmt.Println(g)
24 fmt.Println(g.Area())
25 }
26
27 func main() {
28 s := Square{Width: 3, Height: 4}
29
30 // The Square struct type implements the Geometry interface so we can
31 // use instances of these structs as arguments to measure.
32 measure(s)
33 }
The output is:
{3 4}
12
Here’s an example1 of a simple Animal interface.
13.1 The interface{} type
It’s possible to define an interface without any methods. Such an interface is known as an empty interface, and it’s denoted by interface{}. Since there are no methods, all types will satisfy this interface.
That means if you write a function that takes an interface{} value as a parameter, you can supply that function with any value. So, this function:
1 func DoSomething(v interface{}) {
2 // ...
3 }
will accept any parameter whatsoever.
Here’s where it gets confusing: inside of the DoSomething function, what is v’s type? Beginners are led to believe that “v is of any type”, but that is wrong. v is not of any type; it is of interface{} type. Wait, what? When passing a value into the DoSomething function, the Go runtime will perform a type conversion (if necessary), and convert the value to an interface{} value. All values have exactly one type at runtime, and v’s one static type is interface{}.
This should leave you wondering: ok, so if a conversion is taking place, what is actually being passed into a function that takes an interface{} value?
If you understand that an interface value is two words wide and it contains a pointer to the underlying data, that’s typically enough to avoid common pitfalls.
This topic is adapted from blog posts this2 and this3
13.2 Solutions
14. Error
It is idiomatic in Go to use the error interface type as the return type for any error that is going to be returned from a function or method. This interface is used by all the functions and methods in the standard library that return errors.
Go code uses error values to indicate an abnormal state. For example, the os.Open function returns a non-nil error value when it fails to open a file.
func Open(name string) (file *File, err error)
The following code uses os.Open to open a file. If an error occurs it calls log.Fatal to print the error message and stop.
1 f, err := os.Open("filename.ext")
2 if err != nil {
3 log.Fatal(err)
4 }
5 // do something with the open *File f
Handling errors that are returned from functions and methods starts by checking if the returned interface value of type error is not nil. Thus in the above code, the value of the err variable is compared to the value of nil. If the value is not nil, then there was an error.
In Go, error handling is important. The language’s design and conventions encourage you to explicitly check for errors where they occur (as distinct from the convention in other languages of throwing exceptions and sometimes catching them). In some cases this makes Go code verbose.
15. io, os and net/http - Getting started
15.1 io
Usage: import "io"
Package io provides basic interfaces to I/O primitives.
func Copy(dst Writer, src Reader) (written int64, err error)
Copy copies from src to dst until either EOF is reached on src or an error occurs. It returns the number of bytes copied and the first error encountered while copying, if any.
A successful Copy returns err == nil, not err == EOF. Because Copy is defined to read from src until EOF, it does not treat an EOF from Read as an error to be reported.
15.2 os
Usage: import "os"
Package os provides a platform-independent interface to operating system functionality.
Stdin, Stdout, and Stderr are open Files pointing to the standard input, standard output, and standard error file descriptors.
Hence os.Stdout will write to the standard output.
15.3 TCP and net/http
Networking usually implies TCP/IP, the way in which millions of machines communicate back and forth across the Internet. Network communication is conceptualized at different layers - data link, network, transport and application. The application layer is the world of telnet, FTP, email protocols, and much more.
15.3.1 Basic Networking
Let us talk a little bit about basic networking.
Our discussion of networking focuses on both sides of a client-server relationship. The client requests that some action be performed, and the server performs the action and responds to the client. A dedicated server spends its lifetime waiting for messages and answering them. A common implementation of the request-response model is between World Wide Web browsers and World Wide Web servers. When a user selects a Web site to browse through a browser (the client application), a request is sent to the appropriate Web server (the server application). The server normally responds to the client by sending an appropriate HTML Web page.
15.3.2 Port
A port is not a physical device, but an abstraction to facilitate communication between a server and a client.
Ports are described by a 16-bit integer value. Hence, a machine can have a maximum of 65536 port numbers (ranging from 0 to 65535). The port numbers are divided into three ranges: the Well Known Ports, the Registered Ports, and the Dynamic and/or Private Ports. The Well Known Ports are those from 0 through 1023 (for example, port no. 80 is for http, port no. 25 is for smtp and so on). The Registered Ports are those from 1024 through 49151. The Dynamic and/or Private Ports are those from 49152 through 65535.
15.3.3 Internet Addresses
These are the numerical host addresses that consist of four bytes such as 132.163.4.102. The IP address 127.0.0.1 (localhost) is a special address, called the local loopback address, which denotes the local machine.
15.3.4 net/http
Usage: import "net/http"
Package http provides HTTP client and server implementations.
Get, Head, Post, and PostForm make HTTP (or HTTPS) requests.
func Get(url string) (resp *Response, err error)
Get issues a GET to the specified URL. An error is returned if there was an HTTP protocol error. When err is nil, resp always contains a non-nil resp.Body. Caller should close resp.Body when done reading from it.
The Response2 structure among others has the field StatusCode which is an int (e.g. 200), a field Body that represents the response body and Close which should be called by the caller to close the Body.
The http.StatusOK is a constant with the value of 200.
An example:
1 package main
2
3 import (
4 "io"
5 "log"
6 "net/http"
7 "os"
8 )
9
10 func main() {
11 resp, err := http.Get("http://www.google.com/robots.txt")
12 if err != nil {
13 log.Fatal(err)
14 }
15
16 defer resp.Body.Close()
17
18 if resp.StatusCode != http.StatusOK {
19 log.Fatal(resp.Status)
20 }
21
22 _, err = io.Copy(os.Stdout, resp.Body)
23 if err != nil {
24 log.Fatal(err)
25 }
26 }
16. Random Numbers
Go’s math/rand package provides pseudo-random number generation.
Random numbers are generated by a Source. Top-level functions, such as Float64 and Int, use a default shared Source that produces a deterministic sequence of values each time a program is run. Use the Seed function to initialize the default Source if different behavior is required for each run. The default Source is safe for concurrent use by multiple goroutines.
Usage: import "math/rand"
func Intn(n int) int
Intn returns, as an int, a non-negative pseudo-random number in [0,n) from the default Source. It panics if n <= 0.
func Float64() float64
Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) from the default Source.
Let us look at an example:
1 package main
2
3 import (
4 "fmt"
5 "math/rand"
6 )
7
8 func main() {
9 fmt.Println(rand.Intn(500))
10 fmt.Println(rand.Float64())
11 }
In the above program, rand.Intn returns a random int n, 0 <= n < 500.
When you run the above program the output is always the same. I got 81 and 0.9405090880450124 no matter how many times I ran the program.
To make the pseudo-random generator deterministic, give it a well-known seed. To do that let us look at:
func NewSource(seed int64) Source
NewSource returns a new pseudo-random Source seeded with the given value.
Let us modify our above program as:
1 package main
2
3 import (
4 "fmt"
5 "math/rand"
6 )
7
8 func main() {
9 fmt.Print(rand.Intn(500))
10 fmt.Println(rand.Float64())
11
12 s1 := rand.NewSource(21)
13 r1 := rand.New(s1)
14 fmt.Println(r1.Intn(500))
15 fmt.Println(r1.Float64())
16 }
Now the output I get is always:
810.9405090880450124
3000.8375676569149538
17. File Handling
17.1 Package ioutil
Usage: import "io/ioutil"
The function ioutil.ReadFile(filename string) reads the entire contents of the file filename into memory.
Type and store the following program in the same folder where you had written hello.go:
1 package main
2
3 import (
4 "fmt"
5 "io/ioutil"
6 "log"
7 )
8
9 func check(e error) {
10 if e != nil {
11 log.Fatal(e)
12 }
13 }
14
15 func main() {
16 dat, err := ioutil.ReadFile("hello.go")
17 check(err)
18 fmt.Print(string(dat))
19 }
The program will output the contents of the file hello.go.
func ReadFile(filename string) ([]byte, error)
ReadFile reads the file named by filename and returns the contents. A successful call returns err == nil, not err == EOF. Because ReadFile reads the whole file, it does not treat an EOF from Read as an error to be reported.
17.2 Package os
Usage: import "os"
Documentation: http://golang.org/pkg/os/
Package os provides a platform-independent interface to operating system functionality.
Let us look at some programs in this section.
17.2.1 read_file_os1.go
First write the program read_file_os1.go in the same folder where you had written hello.go as follows:
1 package main
2
3 import (
4 "fmt"
5 "log"
6 "os"
7 )
8
9 func check(e error) {
10 if e != nil {
11 log.Fatal(e)
12 }
13 }
14
15 func main() {
16 file, err := os.Open("hello.go") // For read access
17 check(err)
18 defer file.Close()
19
20 // get the file size
21 stat, err := file.Stat()
22 check(err)
23
24 // read the file
25 bs := make([]byte, stat.Size())
26 _, err = file.Read(bs)
27 check(err)
28
29 str := string(bs) // type string
30 fmt.Println(str)
31 }
The above program is an example of how to read the contents of a file and display them on the terminal.
17.2.1.1 Stat
1 func (f *File) Stat() (fi FileInfo, err error)
Stat returns the FileInfo structure describing file. If there is an error, it will be of type *PathError.
17.2.1.2 FileInfo
A FileInfo describes a file and is returned by Stat and Lstat.
Here’s the FileInfo interface:
1 type FileInfo interface {
2 Name() string // base name of the file
3 Size() int64 // length in bytes for regular files;
4 // system-dependent for others
5 Mode() FileMode // file mode bits
6 ModTime() time.Time // modification time
7 IsDir() bool // abbreviation for Mode().IsDir()
8 Sys() interface{} // underlying data source (can return nil)
9 }
Observe that Size() gives us the length in bytes for regular files.
17.2.2 read_file_os2.go
Next write the second program read_file_os2.go in the same folder where you had written hello.go as follows:
1 package main
2
3 import (
4 "fmt"
5 "log"
6 "os"
7 )
8
9 func check(e error) {
10 if e != nil {
11 log.Fatal(e)
12 }
13 }
14
15 func main() {
16 file, err := os.Open("hello.go") // For read access
17 check(err)
18 defer file.Close()
19
20 //The file's data can then be read into a slice
21 //of bytes. Read and Write take their byte counts
22 //from the length of the argument slice.
23 //Read some bytes from the beginning of
24 //the file. Allow up to 5 to be read but
25 //also note how many actually were read.
26 slice1 := make([]byte, 5)
27 n1, err := file.Read(slice1)
28 check(err)
29 fmt.Printf("%d bytes: %s\n", n1, string(slice1))
30
31 //You can also Seek to a known location in
32 //the file and Read from there.
33 o2, err := file.Seek(6, 0)
34 check(err)
35 slice2 := make([]byte, 2)
36 n2, err := file.Read(slice2)
37 check(err)
38 fmt.Printf("%d bytes @ %d: %s\n", n2, o2, string(slice2))
39 }
Part of the output is shown below:
5 bytes: // Si
2 bytes @ 6: gl
17.2.2.1 seek function:
1 func (f *File) Seek(offset int64, whence int) (ret int64, err error)
Seek sets the offset for the next Read or Write on file to offset, interpreted according to whence: 0 means relative to the origin of the file, 1 means relative to the current offset, and 2 means relative to the end. It returns the new offset and an error, if any.
Note: There is no built-in rewind, but Seek(0, 0) accomplishes this.
17.2.3 write_file_os.go
The next program write_file_os.go is an example on how we can create a file:
1 package main
2
3 import (
4 "log"
5 "os"
6 )
7
8 func check(e error) {
9 if e != nil {
10 log.Fatal(e)
11 }
12 }
13
14 func main() {
15 file, err := os.Create("test.txt")
16 check(err)
17 defer file.Close()
18
19 file.WriteString("test")
20
21 //Issue a Sync to flush writes
22 //to stable storage.
23 file.Sync()
24 }
17.2.3.1 Create
1 func Create(name string) (file *File, err error)
Create creates the named file mode 0666 (before umask), truncating it if it already exists. If successful, methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR. If there is an error, it will be of type *PathError.
17.2.3.2 WriteString
1 func (f *File) WriteString(s string) (ret int, err error)
WriteString writes the contents of string s.
17.2.4 read_dir_os.go
The next program read_dir_os.go is an example on how to get the contents of a directory:
1 package main
2
3 import (
4 "fmt"
5 "log"
6 "os"
7 )
8
9 func check(e error) {
10 if e != nil {
11 log.Fatal(e)
12 }
13 }
14
15 func main() {
16 dir, err := os.Open(".")
17 check(err)
18 defer dir.Close()
19
20 fileInfos, err := dir.Readdir(-1)
21 check(err)
22 for _, fi := range fileInfos {
23 fmt.Println(fi.Name())
24 }
25 }
17.2.4.1 Readdir
1 func (f *File) Readdir(n int) (fi []FileInfo, err error)
Readdir reads the contents of the directory associated with file and returns a slice of up to n ``FileInfo values, in directory order. Subsequent calls on the same file will yield further FileInfos.
If n > 0, Readdir returns at most n FileInfo structures. In this case, if Readdir returns an empty slice, it will return a non-nil error explaining why. At the end of a directory, the error is io.EOF.
If n <= 0, Readdir returns all the FileInfo from the directory in a single slice. In this case, if Readdir succeeds (reads all the way to the end of the directory), it returns the slice and a nil error. If it encounters an error before the end of the directory, Readdir returns the FileInfo read until that point and a non-nil error.
17.2.4.2 Name()
Name() returns a string which is the base name of the file.
17.3 Package bufio
Usage: import "bufio"
Documentation: bufio
Package bufio implements buffered I/O. It wraps an io.Reader or io.Writer object, creating another object (Reader or Writer) that also implements the interface but provides buffering and some help for textual I/O.
Let us write a program read_bufio.go as follows:
1 package main
2
3 import (
4 "bufio"
5 "fmt"
6 "log"
7 "os"
8 )
9
10 func check(e error) {
11 if e != nil {
12 log.Fatal(e)
13 }
14 }
15
16 func main() {
17 file, err := os.Open("hello.go")
18 check(err)
19 defer file.Close()
20
21 reader := bufio.NewReader(file)
22
23 b4, err := reader.Peek(5)
24 check(err)
25 fmt.Printf("5 bytes: %s\n", string(b4))
26 }
17.3.1 NewReader
1 func NewReader(rd io.Reader) *Reader
NewReader returns a new Reader whose buffer has the default size.
17.3.2 Peek
1 func (b *Reader) Peek(n int) ([]byte, error)
Peek returns the next n bytes without advancing the reader. The bytes stop being valid at the next read call. If Peek returns fewer than n bytes, it also returns an error explaining why the read is short. The error is ErrBufferFull if n is larger than b’s buffer size.
Now let us write a program write_bufio.go as follows:
1 package main
2
3 import (
4 "bufio"
5 "fmt"
6 "log"
7 "os"
8 )
9
10 func check(e error) {
11 if e != nil {
12 log.Fatal(e)
13 }
14 }
15
16 func main() {
17 f, err := os.Create("test2.txt")
18 check(err)
19 defer f.Close()
20
21 w := bufio.NewWriter(f)
22 n4, err := w.WriteString("buffered\n")
23 fmt.Printf("wrote %d bytes\n", n4)
24
25 w.Flush()
26 }
The output is:
wrote 9 bytes
17.3.3 NewWriter
1 func NewWriter(wr io.Writer) *Writer
NewWriter returns a new Writer whose buffer has the default size.
Original file contents:
text text text text text
text text text text text
text text word text text
text text text text text
text text text text text
Modified file contents:
text text text text text
text text text text text
text text inserted word text text
text text text text text
text text text text text
17.4 Solutions
Exercise 17 Solution:
Exercise 18 solution:
WAIT… If you have liked the course so far, I am confident that you will like the latter part of the course too. This is where the fun begins.
Glad to have you back…
18. JSON and Go
18.1 JSON
JSON stands for JavaScript Object Notation.
JSON is syntax for storing and exchanging text information, much like XML. JSON is smaller than XML, and faster and easier to parse. JSON is language independent. Here’s an example:
1 {
2 "employees": [
3 { "firstName":"John" , "lastName":"Doe" },
4 { "firstName":"Anna" , "lastName":"Smith" },
5 { "firstName":"Peter" , "lastName":"Jones" }
6 ]
7 }
The employee object is an array of 3 employee records (objects).
JSON data is written as name/value pairs. A name/value pair consists of a field name (in double quotes), followed by a colon and followed by a value:
"firstName" : "Satish"
JSON values can be:
- A number (integer or floating point)
- A string (in double quotes)
- A Boolean (true or false)
- An array (in square brackets)
- An object (in curly brackets)
- null
JSON objects are written inside curly brackets, Objects can contain multiple name/values pairs:
{ "firstName":"Satish" , "lastName":"Talim" }
18.2 Package json
Usage: import "encoding/json"
Package json implements encoding and decoding of JSON objects.
18.2.1 Encoding
To encode JSON data we use the Marshal function.
func Marshal(v interface{}) ([]byte, error)
Let’s look at an example.
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type User struct {
9 UserName string
10 EmailID string
11 Password string
12 }
13
14 func main() {
15 u := User{UserName: "IndianGuru", EmailID: "satishtalim@gmail.com", Password: "password"}
16 m, _ := json.Marshal(u)
17 fmt.Println(string(m))
18 }
The output is:
{"UserName":"IndianGuru","EmailID":"satishtalim@gmail.com","Password":"password"}
In the above program we have a simple struct, we create a new instance of this struct and encode it.
18.2.2 Struct tags
1 type Person struct {
2 UserName string `json:"user_name"`
3 EmailID string
4 Password string `json:"-"`
5 }
When we use the Marshal function on a struct instance it produces JSON.
In the above example, a field appears in JSON as key “user_name” and the Password field is ignored by this package.
In the previous example json1.go the password got printed. However, we don’t want to print the password. Here’s the changed code:
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type User struct {
9 UserName string `json:"user_name"`
10 EmailID string
11 Password string `json:"-"`
12 }
13
14 func main() {
15 u := User{UserName: "IndianGuru", EmailID: "satishtalim@gmail.com", Password: "password"}
16 m, _ := json.Marshal(u)
17 fmt.Println(string(m))
18 }
The output is:
{"user_name":"IndianGuru","EmailID":"satishtalim@gmail.com"}
What if I don’t want to show the UserName if it is empty? Here’s the modified code:
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type User struct {
9 UserName string `json:"first_name,omitempty"`
10 EmailID string
11 Password string `json:"-"`
12 }
13
14 func main() {
15 u := User{UserName: "", EmailID: "", Password: "password"}
16 m, _ := json.Marshal(u)
17 fmt.Println(string(m))
18 }
The output is:
{"EmailID":""}
The JSON parser also accepts a flag in the tag to let it know what to do if the field is empty. The omitempty flag tells it to not include the JSON value in the output if it’s the “zero-value” for that type.
The “zero-value” for numbers is 0, for strings it’s the empty string, for maps, slices and pointers it’s nil. This is how you include the omitempty flag.
1 type MyStruct struct {
2 SomeField string `json:"some_field,omitempty"`
3 }
Notice that the flag goes inside the quotes.
If the SomeField was an empty string, and you converted it to JSON, some_field wouldn’t be included in the output at all.
What if I want to retain the field name EmailID and at the same time use the omitempty flag? Here’s the modified code:
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type User struct {
9 UserName string `json:"first_name,omitempty"`
10 EmailID string `json:",omitempty"`
11 Password string `json:"-"`
12 }
13
14 func main() {
15 u := User{UserName: "", EmailID: "", Password: "password"}
16 m, _ := json.Marshal(u)
17 fmt.Println(string(m))
18 }
The output is:
{}
Let’s look at another example:
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 "os"
7 )
8
9 type Response1 struct {
10 Page int
11 Conf []string
12 }
13
14 type Response2 struct {
15 Page int `json:"page"` // Field appears in JSON as key "page"
16 Conf []string `json:"conf"` // Field appears in JSON as key "conf"
17 }
18
19 func main() {
20 // encoding basic data types to JSON strings
21 bolB, _ := json.Marshal(false)
22 fmt.Println(string(bolB))
23
24 intB, _ := json.Marshal(3)
25 fmt.Println(string(intB))
26
27 fltB, _ := json.Marshal(87.23)
28 fmt.Println(string(fltB))
29
30 strB, _ := json.Marshal("go class")
31 fmt.Println(string(strB))
32
33 // encoding a slice
34 slcD := []string{"gophercon", "gopherconindia", "go china"}
35 slcB, _ := json.Marshal(slcD)
36 fmt.Println(string(slcB))
37
38 // encoding a map
39 // the map's key type must be a string
40 mapD := map[string]int{"pune": 5, "franklin": 7}
41 mapB, _ := json.Marshal(mapD)
42 fmt.Println(string(mapB))
43
44 // The JSON package can automatically encode your
45 // custom data types. It will only include exported
46 // fields in the encoded output and will by default
47 // use those names as the JSON keys.
48 res1D := &Response1{
49 Page: 1,
50 Conf: []string{"gophercon", "gopherconindia", "go china"}}
51 res1B, _ := json.Marshal(res1D)
52 fmt.Println(string(res1B))
53
54 // You can use tags on struct field declarations to
55 // customize the encoded JSON key names. Check the
56 // definition of Response2 above to see an example of
57 // such tags.
58 res2D := &Response2{
59 Page: 1,
60 Conf: []string{"gophercon", "gopherconindia", "go china"}}
61 res2B, _ := json.Marshal(res2D)
62 fmt.Println(string(res2B))
63 }
The output is:
false
3
87.23
"go class"
["gophercon","gopherconindia","go china"]
{"franklin":7,"pune":5}
{"Page":1,"Conf":["gophercon","gopherconindia","go china"]}
{"page":1,"conf":["gophercon","gopherconindia","go china"]}
18.2.3 Decoding
To decode JSON data we use the Unmarshal function.
func Unmarshal(data []byte, v interface{}) error
We must first create a place where the decoded data will be stored.
var m Message
and call json.Unmarshal, passing it a []byte of JSON data and a pointer to m.
err := json.Unmarshal(b, &m)
If b contains valid JSON that fits in m, after the call err will be nil and the data from b will have been stored in the struct m, as if by an assignment like:
1 m = Message {
2 Name: "Alice",
3 Body: "Hello",
4 Time: 1294706395881547000,
5 }
How does Unmarshal identify the fields in which to store the decoded data? For a given JSON key “Foo”, Unmarshal will look through the destination struct’s fields to find (in order of preference):
- An exported field with a tag of “Foo” (see the Go spec for more on struct tags),
- An exported field named “Foo”, or
- An exported field named “FOO” or “FoO” or some other case-insensitive match of “Foo”.
What happens when the structure of the JSON data doesn’t exactly match the Go type?
1 b := []byte(`{"Name":"Bob","Food":"Pickle"}`)
2 var m Message
3 err := json.Unmarshal(b, &m)
Unmarshal will decode only the fields that it can find in the destination type. In this case, only the Name field of m will be populated, and the Food field will be ignored. This behavior is particularly useful when you wish to pick only a few specific fields out of a large JSON blob. It also means that any unexported fields in the destination struct will be unaffected by Unmarshal.
Unmarshal stores in the interface value: map[string]interface{}, for JSON objects.
Here’s an example:
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type Response2 struct {
9 Page int `json:"page"`
10 Conf []string `json:"conf"`
11 }
12
13 func main() {
14 // We can also decode JSON into custom data types.
15 // This has the advantages of adding additional
16 // type-safety to our programs and eliminating the
17 // need for type assertions when accessing the decoded data.
18 // `Unmarshal` will decode only the fields that it can find
19 // in the destination type. In this case, only the Page and
20 // Conf field of res will be populated, and the Food field
21 // will be ignored.
22 str := `{"page": 1, "conf": ["gophercon", "gopherconindia"], "Food":"Pickle"}`
23 res := &Response2{}
24 json.Unmarshal([]byte(str), &res)
25 fmt.Println(res)
26 fmt.Println(res.Conf[0])
27 }
The output is:
&{1 [gophercon gopherconindia]}
gophercon
18.2.4 Streaming Encoders and Decoders
The json package provides Decoder and Encoder types to support the common operation of reading and writing streams of JSON data. The NewDecoder and NewEncoder functions wrap the io.Reader and io.Writer interface types.
func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder
func (enc *Encoder) Encode(v interface{}) error
Encode1 writes the JSON encoding of v to the stream, followed by a newline character.
func (dec *Decoder) Decode(v interface{}) error
Decode2 reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
Previously, to encode JSON data we had used the Marshal function.
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 )
7
8 type User struct {
9 UserName string
10 EmailID string
11 Password string
12 }
13
14 func main() {
15 u := User{UserName: "IndianGuru", EmailID: "satishtalim@gmail.com", Password: "password"}
16 m, _ := json.Marshal(u)
17 fmt.Println(string(m))
18 }
The output was:
{"UserName":"IndianGuru","EmailID":"satishtalim@gmail.com","Password":"password"}
Let’s rewrite the above program using json.NewEncoder as follows:
1 package main
2
3 import (
4 "encoding/json"
5 "log"
6 "os"
7 )
8
9 type User struct {
10 UserName string
11 EmailID string
12 Password string
13 }
14
15 func main() {
16 u := User{UserName: "IndianGuru", EmailID: "satishtalim@gmail.com", Password: "password"}
17
18 // Replace the following lines of json1.go
19 // m, _ := json.Marshal(u)
20 // fmt.Println(string(m))
21 // with:
22 enc := json.NewEncoder(os.Stdout)
23 if err := enc.Encode(u); err != nil {
24 log.Println(err)
25 }
26 }
The output is the same as before:
{"UserName":"IndianGuru","EmailID":"satishtalim@gmail.com","Password":"password"}
These Encoder and Decoder types can be used in a broad range of scenarios, such as reading and writing to HTTP connections, WebSockets, or files.
18.3 A Fun, Weather Forecast Go Web App
We shall build a fun Go app that displays the current weather forecast for a given city.
18.3.1 Register for an account at Forecast for Developers
Go to https://developer.forecast.io/register3 and register for an account. We shall use their free plan that allows us to use 1000 calls to their API per day. As such, there is no need to enter your credit card details. Also, this page gives you your very own API key that you will use in your program.
18.3.1.1 Study the API documentation
We need to read thro’ the API documentation4 of the Forecast for Developers.
Very briefly:
The Forecast Call
https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE?units=ca
APIKEY should be your API key as mentioned above. LATITUDE and LONGITUDE should be the geographic coordinates of a location in decimal degrees.
The response will be a JSON-formatted object with the following properties defined:
-
latitude: The requested latitude. -
longitude: The requested longitude. -
timezone: The timezone name for the requested location (e.g. America/New_York). -
offset: The current timezone offset in hours from GMT. -
currently: A data point (see below) containing the current weather conditions at the requested location.
The following JSON Schema of the API is very informative - forecast.json5
Data Points
A data point object represents the various weather phenomena occurring at a specific instant of time, and has many varied properties. All of these properties (except time) are optional, and will only be set if we have that type of information for that location and time.
The following JSON Schema of the API is very informative - datapoint.json6
We shall use the two schema (datapoint.json and forecast.json) in our program.
Let us define our structs DataPoint and Forecast based on these two schema as follows:
1 type DataPoint struct {
2 Time float64
3 Summary string
4 Icon string
5 SunriseTime float64
6 SunsetTime float64
7 PrecipIntensity float64
8 PrecipIntensityMax float64
9 PrecipIntensityMaxTime float64
10 PrecipProbability float64
11 PrecipType string
12 PrecipAccumulation float64
13 Temperature float64
14 TemperatureMin float64
15 TemperatureMinTime float64
16 TemperatureMax float64
17 TemperatureMaxTime float64
18 DewPoint float64
19 WindSpeed float64
20 WindBearing float64
21 CloudCover float64
22 Humidity float64
23 Pressure float64
24 Visibility float64
25 Ozone float64
26 }
27
28 type Forecast struct {
29 Latitude float64
30 Longitude float64
31 Timezone string
32 Offset float64
33 Currently DataPoint
34 Junk string
35 }
Understanding url query string
A query string is a part of an URL that contains data that can be passed to web applications. This data needs to be encoded, and this encoding is done using url.QueryEscape. It performs what is also commonly called URL encoding7.
1 // addr is a string which contains our address
2 addr := "Pune,India"
3
4 // QueryEscape escapes the addr string so
5 // it can be safely placed inside a URL query
6 safeAddr := url.QueryEscape(addr)
7 fmt.Println(safeAddr) // Pune%2CIndia
With url.QueryEscape our address “Pune,India” becomes “Pune%2CIndia” where %2C is the ASCII keycode in hexadecimal for a comma (,).
The Google Geocoding API
We shall use The Google Geocoding API8 to help us convert addresses (like “1600 Amphitheatre Parkway, Mountain View, CA”) into geographic coordinates (like latitude 37.423021 and longitude -122.083739).
To access the Geocoding API over HTTP, use:
http://maps.googleapis.com/maps/api/geocode/output?parameters
where output may be either of the following values:
- json (recommended) indicates output in JavaScript Object Notation (JSON)
- xml indicates output as XML
Some parameters are required while some are optional. As is standard in URLs, parameters are separated using the ampersand (&) character.
Required parameters in a geocoding request:
address — The street address that you want to geocode, in the format used by the national postal service of the country concerned. Additional address elements such as business names and unit, suite or floor numbers should be avoided.
We are not using any optional parameters in our geocoding request.
1 // Geocoding API
2 fullUrl := fmt.Sprintf("http://maps.googleapis.com/maps/api/geocode/json?address=%s", safe\
3 Addr)
4 fmt.Println(fullUrl)
The output is:
http://maps.googleapis.com/maps/api/geocode/json?address=Pune%2C+India
In your browser, open the site http://maps.googleapis.com/maps/api/geocode/json?address=Pune%2C+India the browser output is a huge blob of JSON. This may be difficult to look at in the browser, unless you have the JSONView plugin installed. These extensions are available for Firefox9 and Chrome10. With the extension installed you should be able to see a better view of the JSON returned.
Build the http request
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
NewRequest11 returns a new Request given a method, URL, and an optional body.
1 // Build the http request
2 req, err1 := http.NewRequest("GET", fullUrl, nil)
3 check(err1, "NewRequest:")
Also, we write a check function that checks for errors, if any:
1 func check(e error, str string) {
2 if e != nil {
3 log.Fatal(str, " ", e)
4 return
5 }
6 }
Create a Client
For control over HTTP client headers, redirect policy, and other settings, create a Client12. A Client is an HTTP client:
client := &http.Client{}
Send the request via a client
func (c *Client) Do(req *Request) (resp *Response, err error)
Do sends an HTTP request and returns an HTTP response.
1 resp, err2 := client.Do(req)
2 check(err2, "Do:")
3
4 // Callers should close resp.Body
5 // when done reading from it
6 // Defer the closing of the body
7 defer resp.Body.Close()
Create a Response struct
Observe the JSON output when you open the site http://maps.googleapis.com/maps/api/geocode/json?address=Pune%2C+India in your browser. There is a results array, within which there is geometry, then location which finally contains lat and lng.
Therefore let’s define our struct as follows:
1 type Response struct {
2 Results []struct {
3 Geometry struct {
4 Location struct {
5 Lat float64
6 Lng float64
7 }
8 }
9 }
10 }
Use a streaming Decoder
1 // Use json.Decode for reading streams of JSON data
2 if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
3 log.Println(err)
4 }
Extract the latitude and logitude
1 // lat, lng as float64
2 lat := res.Results[0].Geometry.Location.Lat
3 lng := res.Results[0].Geometry.Location.Lng
Use the Forecast API
Declare a global constant APIKey as:
const APIKey string = "yourapikey"
safeLatLng := url.QueryEscape(fmt.Sprintf("%.13f,%.13f", lat, lng))
url := fmt.Sprintf("https://api.forecast.io/forecast/%s/%s?units=ca", APIKey, safeLatLng)
- Remember to replace
APIKeyabove with your actual api key. -
%.13fis used to convertfloat64to astring -
?units=ca- the API request was optionally modified through the use of query parameterunits=cawill return temperatures in degrees Celsius.
Use http.Get
func Get(url string) (resp *Response, err error)
Get14 issues a GET to the specified URL.
1 resp, err := http.Get(url)
2 check(err, "Get:")
3 defer resp.Body.Close()
Use the Forecast struct to get the results
1 var f Forecast
2 if err := json.NewDecoder(resp.Body).Decode(&f); err != nil {
3 log.Println(err)
4 }
5
6 fmt.Println("The Weather at ", addr)
7 fmt.Println("Timezone = ", f.Timezone)
8 fmt.Println("Temp in Celsius = ", f.Currently.Temperature)
9 fmt.Println("Summary = ", f.Currently.Summary)
Here’s the full program:
1 package main
2
3 import (
4 "encoding/json"
5 "fmt"
6 "log"
7 "net/http"
8 "net/url"
9 )
10
11 type DataPoint struct {
12 Time float64
13 Summary string
14 Icon string
15 SunriseTime float64
16 SunsetTime float64
17 PrecipIntensity float64
18 PrecipIntensityMax float64
19 PrecipIntensityMaxTime float64
20 PrecipProbability float64
21 PrecipType string
22 PrecipAccumulation float64
23 Temperature float64
24 TemperatureMin float64
25 TemperatureMinTime float64
26 TemperatureMax float64
27 TemperatureMaxTime float64
28 DewPoint float64
29 WindSpeed float64
30 WindBearing float64
31 CloudCover float64
32 Humidity float64
33 Pressure float64
34 Visibility float64
35 Ozone float64
36 }
37
38 type Forecast struct {
39 Latitude float64
40 Longitude float64
41 Timezone string
42 Offset float64
43 Currently DataPoint
44 Junk string
45 }
46
47 type Response struct {
48 Results []struct {
49 Geometry struct {
50 Location struct {
51 Lat float64
52 Lng float64
53 }
54 }
55 }
56 }
57
58 // Replace the text `yourapikey` below with your actual api key
59 const APIKey string = "yourapikey"
60
61 func main() {
62 Get()
63 }
64
65 func check(e error, str string) {
66 if e != nil {
67 log.Fatal(str, " ", e)
68 return
69 }
70 }
71
72 func Get() {
73 var addr string
74
75 fmt.Println("Enter City eg. Pune, India: ")
76 fmt.Scanf("%s", &addr)
77
78 // QueryEscape escapes the addr string so
79 // it can be safely placed inside a URL query
80 safeAddr := url.QueryEscape(addr)
81
82 // Geocoding API
83 fullUrl := fmt.Sprintf("http://maps.googleapis.com/maps/api/geocode/json?address=%s", saf\
84 eAddr)
85
86 // Build the http request
87 req, err1 := http.NewRequest("GET", fullUrl, nil)
88 check(err1, "NewRequest:")
89
90 // For control over HTTP client headers,
91 // redirect policy, and other settings,
92 // create a Client
93 // A Client is an HTTP client
94 client := &http.Client{}
95
96 // Send the request via a client
97 // Do sends an HTTP request and
98 // returns an HTTP response
99 resp, err2 := client.Do(req)
100 check(err2, "Do:")
101
102 // Callers should close resp.Body
103 // when done reading from it
104 // Defer the closing of the body
105 defer resp.Body.Close()
106
107 var res Response
108
109 // Use json.Decode or json.Encode for reading or writing streams of JSON data
110 if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
111 log.Println(err)
112 }
113
114 // lat, lng as float64
115 lat := res.Results[0].Geometry.Location.Lat
116 lng := res.Results[0].Geometry.Location.Lng
117
118 safeLatLng := url.QueryEscape(fmt.Sprintf("%.13f,%.13f", lat, lng))
119 url := fmt.Sprintf("https://api.forecast.io/forecast/%s/%s?units=ca", APIKey, safeLatLng)
120
121 resp, err := http.Get(url)
122 check(err, "Get:")
123 defer resp.Body.Close()
124
125 var f Forecast
126
127 if err := json.NewDecoder(resp.Body).Decode(&f); err != nil {
128 log.Println(err)
129 }
130
131 fmt.Println("The Weather at ", addr)
132 fmt.Println("Timezone = ", f.Timezone)
133 fmt.Println("Temp in Celsius = ", f.Currently.Temperature)
134 fmt.Println("Summary = ", f.Currently.Summary)
135 }
The output is:
Enter City eg. Pune, India:
Pune, India
The Weather at Pune,
Timezone = Asia/Kolkata
Temp in Celsius = 30.59
Summary = Mostly Cloudy
That’s it!
- http://golang.org/pkg/encoding/json/#Encoder.Encode↩
- http://golang.org/pkg/encoding/json/#Decoder.Decode↩
- https://developer.forecast.io/register↩
- https://developer.forecast.io/docs/v2↩
- https://github.com/kingsfleet/rest-metadata/blob/master/forecast.io/forecast.json↩
- https://github.com/kingsfleet/rest-metadata/blob/master/forecast.io/datapoint.json↩
- http://en.wikipedia.org/wiki/Query_string#URL_encoding↩
- https://developers.google.com/maps/documentation/geocoding/↩
- https://addons.mozilla.org/en-us/firefox/addon/jsonview/↩
- https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc↩
- http://golang.org/pkg/net/http/#NewRequest↩
- http://golang.org/pkg/net/http/#Client↩
- http://mholt.github.io/json-to-go/↩
- http://golang.org/pkg/net/http/#Get↩
19. TCP programming using Go
19.1 TCP Sockets
If you are a client you need an API that will allow you to connect to a service and then to send messages to that service and read replies back from the service.
If you are a server, you need to be able to bind to a port and listen at it. When a message comes in you need to be able to read it and write back to the client.
19.2 Package net
Usage: import "net"
Package net provides a portable interface for network I/O, including TCP/IP and UDP.
Type net.Conn1 interface is a generic stream-oriented network connection. Multiple goroutines may invoke methods on a net.Conn simultaneously.
Type net.TCPConn2 is an implementation of the net.Conn interface for TCP network connections. net.TCPConn has two major methods of interest:
func (c *TCPConn) Write(b []byte) (n int, err error)func (c *TCPConn) Read(b []byte) (n int, err error)
A net.TCPConn is used by both a client and a server to read and write messages.
19.2.1 TCP Client
Once a client has established a TCP address for a service, it “dials” the service. If succesful, the dial returns a net.TCPConn for communication. The client and the server exchange messages on this. Typically a client writes a request to the server using the net.TCPConn, and reads a response from the net.TCPConn. This continues until either (or both) sides close the connection. A TCP connection is established by the client using the function:
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error)
net.DialTCP connects to the remote address raddr on the network net, which must be “tcp”, “tcp4” (IPv4-only i.e. Internet Protocol version 4), or “tcp6” (IPv6-only i.e. Internet Protocol version 6). If laddr is not nil, it is used as the local address for the connection.
Note: Use “tcp” or “tcp4” or “tcp6” depending upon your computer.
A simple example can be provided by a client to a web (HTTP) server.
One of the possible messages that a client can send is the “GET” message. This queries a server for information about the server and a document on that server. The server’s response contains header as well as body, and the connection closes after the response. The request sent to query an HTTP server could be:
GET / HTTP/1.0\r\n\r\n
1 package main
2
3 import (
4 "fmt"
5 "io/ioutil"
6 "log"
7 "net"
8 "os"
9 )
10
11 func check(e error, str string) {
12 if e != nil {
13 log.Fatal(str, " ", e)
14 return
15 }
16 }
17
18 func main() {
19 if len(os.Args) != 2 {
20 fmt.Fprintf(os.Stderr, "Usage: %s host:port ", os.Args[0])
21 os.Exit(1)
22 }
23 service := os.Args[1]
24 tcpAddr, err := net.ResolveTCPAddr("tcp", service)
25 check(err, "ResolveTCPAddr:")
26
27 conn, err := net.DialTCP("tcp", nil, tcpAddr)
28 check(err, "DialTCP:")
29
30 // response contains header as well as body, and the connection closes after the response.
31 _, err = conn.Write([]byte("GET / HTTP/1.0\r\n\r\n"))
32 check(err, "Write:")
33
34 result, err := ioutil.ReadAll(conn)
35 check(err, "ReadAll:")
36
37 fmt.Println(string(result))
38 }
To run this program, type:
go run get_info.go www.golang.com:80
The output is:
HTTP/1.0 302 Found
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Content-Type: text/html; charset=UTF-8
Location: http://www.google.co.in/?gfe_rd=cr&ei=CkNdVf-iMYT5vQS_w4H4CQ
Content-Length: 261
Date: Thu, 21 May 2015 02:29:30 GMT
Server: GFE/2.0
Alternate-Protocol: 80:quic,p=0
Pragma: no-cache
Expires: Tue, 01 Jan 1971 02:00:00 GMT
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.co.in/?gfe_rd=cr&ei=CkNdVf-iMYT5vQS_w4H4CQ">here</A>.
</BODY></HTML>
In this program:
- We use the host:port as www.golang.com:80
-
net.ResolveTCPAddr3 parses
www.golang.com:80as 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/ioutilfunctionReadAllwill look after these issues and return the complete response.
19.2.2 A Daytime server
A server registers itself on a port, and listens on that port. Then it blocks on an “accept” operation, waiting for clients to connect. When a client connects, the accept call returns, with a connection object. The daytime service is very simple and just writes the current time to the client, closes the connection, and resumes waiting for the next client.
1 package main
2
3 import (
4 "fmt"
5 "log"
6 "net"
7 "time"
8 )
9
10 func check(e error, str string) {
11 if e != nil {
12 log.Fatal(str, " ", e)
13 return
14 }
15 }
16
17 func main() {
18 listener, err := net.Listen("tcp", ":6000")
19 check(err, "Listen:")
20
21 for {
22 conn, err := listener.Accept()
23 if err != nil {
24 log.Println(err)
25 conn.Close()
26 break
27 }
28 fmt.Println("Connected to client...")
29
30 daytime := fmt.Sprintf("The Date Time is: %s", time.Now())
31
32 conn.Write([]byte(daytime)) // don't care about return value
33 conn.Close() // we're finished with this client
34 fmt.Println("Client connection closed.")
35 }
36 }
- net.Listen6 listens on port 6000.
-
Accept7 implements the
Acceptmethod in theListenerinterface; it waits for the next call and returns a genericConn. - When a client connects to it, it will respond by sending the daytime string to it and then return to waiting for the next client.
- The server should run forever, so that if any error occurs with a client, the server just ignores that client and carries on. A client could otherwise try to mess up the connection with the server, and bring it down.
Run the telnet client as:
telnet localhost 6000
you should see the date-time at the server location.
20. Unit testing and Benchmarking Go programs
20.1 Testing
Usage: import "testing"
Package testing1 provides support for automated testing of Go packages. It is intended to be used in concert with the go test command.
Let us create a folder mult as follows:
$ mkdir $GOPATH/src/github.com/SatishTalim/mult
Replace SatishTalim with your GitHub id.
Next, create a file named mult.go inside that folder, containing the following Go code:
1 package mult
2
3 func Mult2Ints(i, j int) int {
4 return i * j
5 }
To write a new test suite, create a file whose name ends _test.go (mult_test.go in our case) that contains the TestXxx functions as shown below. Put the file in the same package as the one being tested. The file will be excluded from regular package builds but will be included when the go test command is run.
1 //same package name as source file
2 package mult
3
4 //import go package for testing related functionality
5 import "testing"
6
7 //test function starts with "Test" and takes a pointer to type testing.T
8 func TestMult2Ints_1(t *testing.T) {
9 //try a unit test on function
10 if Mult2Ints(3, 4) != 12 {
11 // log error if it did not work as expected
12 t.Error("Mult2Ints did not work as expected.")
13 } else {
14 // log some info if you want
15 t.Log("one test passed.")
16 }
17 }
18
19 //test function starts with "Test" and takes a pointer to type testing.T
20 func TestMult2Ints_2(t *testing.T) {
21 //Indicate that this test failed and log the string as info
22 t.Error("this is just hardcoded as an error.")
23 }
Please note:
- the file name has to end with
_test.goto 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
importthe packagetesting - all test functions should start with
Testto be run as a test - the tests will be executed in the same order that they are appear in the source
- the test function
TestXxxtake 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.Twithin the test codeError,Errorf,FailNow,Fatal,FatalIfwill indicate to go test that the test has failed
Next in the folder $GOPATH/src/github.com/SatishTalim/mult, run your tests by executing go test at the command line:
$ go test
--- FAIL: Test_Mult2Ints_2 (0.00s)
mult_test.go:16: this is just hardcoded as an error.
FAIL
exit status 1
FAIL github.com/SatishTalim/mult 0.103s
What happened to the first test? By default go test hides all passed results. If you want to see all the results, including the passed tests, execute it with the -v option:
$ go test -v
=== RUN Test_Mult2Ints_1
--- PASS: Test_Mult2Ints_1 (0.00s)
mult_test.go:11: one test passed.
=== RUN Test_Mult2Ints_2
--- FAIL: Test_Mult2Ints_2 (0.00s)
mult_test.go:16: this is just hardcoded as an error.
FAIL
exit status 1
FAIL github.com/SatishTalim/mult 0.073s
20.2 Benchmarks
Benchmark tests allow you to check the performance of your code. It is useful when you want to test the performance between different solutions to the same problem and see which solution performs better.
Please note:
- benchmark tests must have the signature
func BenchmarkXxx ( *testing.B). You can have any combination of alphanumeric characters for the Xxx part, the only constraint it should not begin with a small alphabet, [a-z] -
go testdoes not run the benchmark tests by default - benchmark tests are executed by the
go testcommand when its-benchflag is provided -
go testautomatically 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.Nwithin a loop to repeatedly run a certain function - no benchmark results if all the unit tests do not pass successfully. So either make sure all tests pass successfully, which of course should be the plan in any case
Add a new file shown below:
1 //same package name as source file
2 package mult
3
4 //import go package for testing related functionality
5 import "testing"
6
7 //benchmark function starts with "Benchmark" and takes a pointer to type testing.B
8 func BenchmarkTheMultIntsFunction(b *testing.B) {
9 //use b.N for looping
10 for i := 0; i < b.N; i++ {
11 Mult2Ints(4, 5)
12 }
13 }
14
15 //benchmark function starts with "Benchmark" and takes a pointer to type testing.B
16 func BenchmarkTimeConsumingFunction(b *testing.B) {
17 //stop the performance timer temporarily while doing initialization
18 b.StopTimer()
19
20 //do any time consuming initialization functions here ...
21 //database connection, reading files, network connection, etc.
22
23 //restart timer
24 b.StartTimer()
25 for i := 0; i < b.N; i++ {
26 Mult2Ints(4, 5)
27 }
28 }
Run the program as follows:
$ go test -v -run="none" -bench .
PASS
Benchmark_TheMultIntsFunction 2000000000 0.65 ns/op
Benchmark_TimeConsumingFunction 2000000000 0.65 ns/op
ok github.com/SatishTalim/mult 2.974s
In our call to , we specified the option passing go test -run the string “none” to make sure no unit tests are run prior to running the specified benchmark functions. Both these options take a regular expression to filter the tests to run. Since there is no unit test function that has none in its name, it eliminates any unit tests from running.
The benchmark function must run the target code b.N times. During benchark execution, b.N is adjusted until the benchmark function lasts long enough to be timed reliably.
The output:
Benchmark_TheMultIntsFunction 2000000000 0.65 ns/op
means that the loop ran 2000000000 times at a speed of 0.65 ns per loop.
The default minimum run time for a benchmark is 1 second. You can see how the framework still ran the test for approximately three seconds.
Let’s look at a set of benchmark functions that reveal the fastest way to convert a float value to a string. In the standard library there are two different ways to convert a float value to a string.
Let us create a folder conv as follows:
$ mkdir $GOPATH/src/github.com/SatishTalim/conv
Replace SatishTalim with your GitHub id.
Next, create a file named conv_test.go inside that folder, containing the following Go code:
1 package conv
2
3 import (
4 "fmt"
5 "testing"
6 )
7
8 // Sample benchmarks to test which function is better for converting
9 // an float into a string. First using the fmt.Sprintf function.
10 func BenchmarkSprintf(b *testing.B) {
11 no := 34.56
12
13 b.ResetTimer()
14
15 for i := 0; i < b.N; i++ {
16 fmt.Sprintf("%f", no)
17 }
18 }
Run the program as follows:
$ go test -v -run="none" -bench="BenchmarkSprintf"
testing: warning: no tests to run
PASS
BenchmarkSprintf 1000000 1365 ns/op
ok github.com/SatishTalim/conv 3.316s
The performance of using the Sprintf function in this context is it takes 1365 nanoseconds on average per call.
The final output from running the benchmark shows ok to represent the benchmark finished properly. Then the name of the code file that was executed is displayed and finally the total time the benchmark ran. The default minimum run time for a benchmark is 1 second. You can see how the framework still ran the test for approximately more than three seconds.
Now, let us write another benchmark function in our source file conv_test.go.
1 package conv
2
3 import (
4 "fmt"
5 "strconv"
6 "testing"
7 )
8
9 // Sample benchmarks to test which function is better for converting
10 // an float into a string. First using the fmt.Sprintf function.
11 func BenchmarkSprintf(b *testing.B) {
12 no := 34.56
13
14 b.ResetTimer()
15
16 for i := 0; i < b.N; i++ {
17 fmt.Sprintf("%f", no)
18 }
19 }
20
21 // BenchmarkFormat provides performance numbers for the
22 // strconv.FormatInt function.
23 func BenchmarkFormat(b *testing.B) {
24 no := 34.56
25
26 b.ResetTimer()
27
28 for i := 0; i < b.N; i++ {
29 strconv.FormatFloat(no, 'f', 2, 64)
30 }
31 }
Run the program as follows:
$ go test -v -run="none" -bench="BenchmarkFormat"
testing: warning: no tests to run
PASS
BenchmarkFormat 1000000 1165 ns/op
ok github.com/SatishTalim/conv 2.235s
The BenchmarkFormat function above, benchmarks the use of the function FormatFloat from the strconv package.
The call to b.ResetTimer, which is used in the two benchmark functions. This method is useful to reset the timer when initialization is required before the code can start executing the loop. To have the most accurate benchmark times you can, use this method.
The results show that the test function runs BenchmarkFormat the fastest at 1165 nanoseconds per operation.
- http://golang.org/pkg/testing/↩
21. Concurrency
Rob Pike - Concurrency is not parallelism1:
When people hear the word concurrency they often think of parallelism, a related but quite distinct concept. In programming, concurrency is the composition of independently executing processes, while parallelism is the simultaneous execution of (possibly related) computations. Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.
Go allows us to write concurrent programs. It provides goroutines and importantly, the ability to communicate between them.
21.1 Goroutines
They’re called goroutines2 because the existing terms—threads, coroutines, processes, and so on—convey inaccurate connotations. A goroutine has a simple model: it is a function executing concurrently with other goroutines in the same address space. It is lightweight, costing little more than the allocation of stack space. And the stacks start small, so they are cheap, and grow by allocating (and freeing) heap storage as required.
Goroutines are multiplexed onto multiple OS threads so if one should block, such as while waiting for I/O, others continue to run. Their design hides many of the complexities of thread creation and management.
Prefix a function or method call with the go keyword to run the call in a new goroutine. When the call completes, the goroutine exits, silently.
go list.Sort() // run list.Sort concurrently; don't wait for it.
An example:
1 package main
2
3 import (
4 "fmt"
5 "math/rand"
6 "time"
7 )
8
9 func f(goRtn int) {
10 for i := 0; i < 3; i++ {
11 fmt.Println("Goroutine ", goRtn, " : Value ", i)
12 amt := time.Duration(rand.Intn(250))
13 time.Sleep(time.Millisecond * amt)
14 }
15 }
16 func main() {
17 for i := 0; i < 3; i++ {
18 go f(i)
19 }
20 var input string
21 fmt.Scanln(&input)
22 }
This program consists of two goroutines. The first goroutine is implicit and is the main function itself. The second goroutine is created when we call go f(0). Normally when we invoke a function our program will execute all the statements in a function and then return to the next line following the invocation. With a goroutine we return immediately to the next line and don’t wait for the function to complete. This is why the call to the Scanln function has been included; without it the program would exit before being given the opportunity to print all the numbers.
Goroutines are lightweight and we can easily create thousands of them. Our program runs 3 goroutines.
We add some delay to the function using time.Sleep3 and rand.Intn4.
f prints out the numbers from 0 to 3, waiting between 0 and 250 ms after each one. The goroutines should now run simultaneously.
The output I get is:
Goroutine 0 : Value 0
Goroutine 1 : Value 0
Goroutine 2 : Value 0
Goroutine 0 : Value 1
Goroutine 2 : Value 1
Goroutine 1 : Value 1
Goroutine 0 : Value 2
Goroutine 2 : Value 2
Goroutine 1 : Value 2
21.2 Channels
Channels provide a way for two goroutines to communicate with one another and synchronize their execution.
Channels have several characteristics: the type of element you can send through a channel, capacity (or buffer size) and direction of communication specified by a <- operator. Thus, you can send values into channels from one goroutine and receive those values into another goroutine with the channel operator, <-.
Channels are first-class values and can be used anywhere like other values: as struct elements, function arguments, function returning values and even like a type for another channel.
1 chan <- v // Send v to channel chan.
2 v := <-chan // Receive from chan, and
3 // assign value to v.
(The data flows in the direction of the arrow.)
Channels must be created using the built-in function make before use:
1 ch := make(chan int)
By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.
Let us look at a simple example:
1 package main
2
3 import "fmt"
4
5 func main() {
6 //Create a new channel with
7 //make(chan val-type). Channels are
8 //typed by the values they convey
9 messages := make(chan string)
10
11 //Send a value into a channel using
12 //the channel <- syntax. Here we send
13 //"ping" to the messages channel we
14 //made above, from a new goroutine
15 go func() { messages <- "ping" }()
16
17 //The <-channel syntax receives a
18 //value from the channel. Here we'll
19 //receive the "ping" message we sent
20 //above and print it out
21 msg := <-messages
22 fmt.Println(msg)
23 }
When we run the program the “ping” message is successfully passed from one goroutine to another via our channel.
By default sends and receives block until both the sender and receiver are ready. This property allowed us to wait at the end of our program for the “ping” message without having to use any other synchronization.
21.2.1 Channel direction
We can specify a direction on a channel type thus restricting it to either sending or receiving. For example if we have the following function signature:
1 func pinger(c chan<- string)
Now c can only be sent to. Attempting to receive from c will result in a compiler error. Similarly we can change signature to this:
1 func printer(c <-chan string)
A channel that doesn’t have these restrictions is known as bi-directional. A bi-directional channel can be passed to a function that takes send-only or receive-only channels, but the reverse is not true.
21.2.2 Unbuffered channel
Let’s understand this with an example:
1 package main
2
3 import "fmt"
4
5 func main() {
6 fmt .Println("From main()")
7
8 // Create a channel to synchronize goroutines
9 done := make(chan bool)
10
11 // Execute fmt.Println in goroutine
12 go func() {
13 sum := 0
14 for i := 0; i < 10000; i++ {
15 sum += i
16 }
17 fmt.Println("From goroutine - done.")
18
19 // Tell the main function everything is done.
20 // This channel is visible inside this goroutine because
21 // it is executed in the same address space.
22 done <- true
23 }()
24
25 <-done // Read operation: Wait for the goroutine to finish
26 }
Observe the output. The line “From main()” is blocked i.e. it is not printed until the goroutine has writen data to the channel. Thus the output you see is:
From main()
From goroutine - done.
both being printed at the same time.
Note: If the channel is unbuffered, the sender blocks until the receiver has received the value.
In the above program, the done channel has no buffer (as we did not specify its capacity). All operations on unbuffered channels block the execution until both sender and receiver are ready to communicate. That’s why unbuffered channels are also called synchronous. In our case the reading operation <-done in the main function will block its execution until the goroutine will write data to the channel. Thus the program ends only after the reading operation (<-done) succeeds.
22. Additional Exercises
1 package main
2
3 import "fmt"
4
5 func main() {
6 x := "text"
7 fmt.Println(x[0]) //print 116
8 }
1 package main
2
3 import "fmt"
4
5 func main() {
6 data := []int{1,2,3}
7 i := 0
8 ++i //error
9 fmt.Println(data[i++]) //error
10 }
1 package main
2
3 import (
4 "fmt"
5 "log"
6 "net/http"
7 "io/ioutil"
8 )
9
10 func check(e error, str string) {
11 if e != nil {
12 log.Fatal(str, " ", e)
13 return
14 }
15 }
16
17 func main() {
18 resp, err := http.Get("https://api.ipify.org?format=json")
19 defer resp.Body.Close()
20 check(err, "http.Get:")
21
22 body, err := ioutil.ReadAll(resp.Body)
23 check(err, "ReadAll:")
24
25 fmt.Println(string(body))
26 }
22.1 Solutions
Exercise A1 Solution1. Another solution for A12.
Exercise A2 Reason: The index operator on a string returns a byte value, not a character (like it’s done in other languages). For more details, read the topic on conversions3.
Exercise A3 Reason: Many languages have increment and decrement operators. Unlike other languages, Go doesn’t support the prefix version of the operations. You also can’t use these two operators in expressions.
Exercise A4 Solution:
Exercise A5 Reason: When you make requests using the standard http library you get a http response variable. If you don’t read the response body you still need to close it. You must do it for empty responses too. It’s very easy to forget especially. Some new Go developers do try to close the response body, but they do it in the wrong place as was done in the given program. The most common why to close the response body is by using a defer call after the http response error check.
Most of the time when your http request fails the resp variable will be nil and the err variable will be non-nil. However, when you get a redirection failure both variables will be non-nil.
A better way is:
1 resp, err := http.Get("https://api.ipify.org?format=json")
2 defer func() {if resp != nil {resp.Body.Close()}}()
3 check(err, "http.Get:")
Exercise A6 Solution.
23. Additional Reading
23.1 Testable Examples in Go
Godoc examples are snippets of Go code that are displayed as package documentation and that are verified by running them as tests. They can also be run by a user visiting the godoc web page for the package and clicking the associated “Run” button.
Appendix A
Downloading Go
Visit the Go project’s downloads page1 and select the binary distribution that matches your operating system and processor architecture.
Install the Go tools
Read through the installation guide2 for Linux, Mac OS X, FreeBSD and Windows.
The Go binary distributions assume they will be installed in /usr/local/go (or c:\Go under Windows), but it is possible to install them in a different location. If you do this, you will need to set the GOROOT environment variable to that directory when using the Go tools.
For example, if you installed Go to your home directory you should add the following commands to $HOME/.profile:
export GOROOT=$HOME/go
export PATH=$PATH:$GOROOT/bin
Under Windows, you may set environment variables through the “Environment Variables” button on the “Advanced” tab of the “System” control panel. Some versions of Windows provide this control panel through the “Advanced System Settings” option inside the “System” control panel.
The Go-environment works with a small number of OS environment variables. They are not required for building the Go-environment, but by setting them and thus overriding the defaults the Go compilation environment can be customized. I have set the following:
| Name | Value |
|---|---|
| GOROOT | C:\go |
| GOOS | windows |
| GOARCH | 386 |
Environment Variables
On Windows:
In the same dialog-window: Edit the PATH-variable as follows:
C:\go\bin; ...rest of PATH...
Test your installation
Check that Go is installed correctly by building a simple program, as follows.
Create a file named hello_world.go in some folder (for now) and put the following program in it:
1 package main
2
3 import "fmt"
4
5 func main() {
6 fmt.Println("Hello, world.")
7 }
Then from the folder where you have saved the file hello_world.go run it with the go tool:
$ go run hello_world.go
Hello, world.
If you see the “Hello, world.” message then your Go installation is working.
Go Code Organization
The go tool3 is the standard way to fetch, build, and install Go packages and commands. The go tool requires you to organize your code in a specific way.
Workspaces
Go code must be kept inside a workspace. A workspace is a directory hierarchy with three directories at its root:
-
srccontains Go source files organized into packages (one package per directory), -
pkgcontains package objects, and -
bincontains executable commands.
The go tool builds source packages and installs the resulting binaries to the pkg and bin directories.
The src subdirectory typically contains version control repositories (such as for Git) that track the development of one or more source packages.
The GOPATH environment variable
The GOPATH environment variable specifies the location of your workspace.
To get started, create a workspace directory and set GOPATH accordingly. Your workspace can be located wherever you like. Note that this must not be the same path as your Go installation.
On my Windows computer, I have set GOPATH=C:\go_projects\go. Next I update my system environment variable PATH to include my workspace bin subdirectory i.e. PATH=%PATH%;%GOPATH%\bin;
We need to do the same on *nix or Mac as well.
mkdir $HOME/go_projects/go
export GOPATH=$HOME/go_projects/go
export PATH=$PATH:$GOPATH/bin
C:\go_projects
\---go
+---bin
+---pkg
\---src
Package paths
The packages from the standard library are given short paths such as fmt and net/http. For your own packages, you must choose a base path that is unlikely to collide with future additions to the standard library or other external libraries. If you have a GitHub account at github.com/user, that should be your base path. We will use
github.com/user as our base path. Create a directory inside your workspace in which to keep the source code. I have created the folder C:\go_projects\go\src\github.com\SatishTalim.
Editing a Go program
Go programs are written as plain text Unicode using the UTF-8 encoding. All of Go’s keywords and operators use ASCII characters; however, Go identifiers can start with any Unicode letter followed by any Unicode letters or digits, so Go programmers can freely use their native language.
A Go program
To compile and run a simple program, first choose a package path and create a corresponding package directory inside your workspace:
$ mkdir $GOPATH/src/github.com/SatishTalim/hello
Next, create a file named hello.go inside that directory, containing the following Go code:
1 package main
2
3 import "fmt"
4
5 func main() {
6 fmt.Printf("Hello, world.\n")
7 }
Now you can build and install that program with the go tool:
$ cd $GOPATH/src/github.com/SatishTalim/hello
$ go install
The go tool will only print output when an error occurs, so if these commands produce no output they have executed successfully.
The above command i.e. go install4 compiles, builds the hello executable binary and then installs that binary to the workspace’s bin directory as hello (or, under Windows, hello.exe).
You can now run the program by typing:
$ hello
Hello, world.
Appendix B - Project: redditnews for Baby Gophers
I am keen to be abreast with what’s happening in the Golang world. To that end, we will write a command-line program (redditnews.go) that fetches and displays the latest headlines from the golang page on Reddit.
The program will:
- make an HTTP request to the Reddit API.
- decode the JSON response into a Go data structure, and
- display each link’s author, score, URL and title.
We will then be building a bare-bones News Reader package (redditnews) that gives us the latest news and headlines from the Golang Sub-Reddit, using Reddit’s API.
Once the project is ready, we will use GMail to send an email which will contain the information fetched by the package redditnews.
Use GitHub
One of the best places to share your code with friends, co-workers, classmates, and complete strangers is GitHub1.
Create an account
On the main screen that shows up, enter your username, your email and create a password. Next, click on Sign up for GitHub. On the next screen, The ‘Free’ plan is automatically chosen for you. Just click on the Finish sign up button. You will receive an email asking you to ‘Confirm your email’. Please do that.
Set up Git
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
If you are new to Git, then learn Git in 15 minutes2.
Next, Set Up Git3 on your computer as per the guide.
Complete Appendix A
I am assuming that you have completed Appendix A.
redditnews.go (First Iteration)
This is the first draft of my program.
In your browser, open the site http://reddit.com/r/golang.json4 the browser output is a huge blob of JSON that we receive from the Golang Subreddit. This may be difficult to look at in the browser, unless you have the JSONView plugin installed. These extensions are available for Firefox5 and Chrome. With the extension installed, here’s a partial view of the JSON:
Now let’s write the first draft of our program.
Make a new folder and cd to it as follows:
$ mkdir $GOPATH/src/github.com/SatishTalim/redditnews
$ cd $GOPATH/src/github.com/SatishTalim/redditnews
In this folder, create a file named redditnews.go, open it in your favorite editor, and add the following lines:
package main
import (
"io"
"log"
"net/http"
"os"
)
func main() {
resp, err := http.Get("http://reddit.com/r/golang.json")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Fatal(resp.Status)
}
_, err = io.Copy(os.Stdout, resp.Body)
if err != nil {
log.Fatal(err)
}
}
- Like all Go programs that need to be executed, our program has a package
main. - Package io6 provides basic interfaces to I/O primitives.
- On an error we use log7. Package
logimplements 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.Fatalfunction 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 variablerespand doing aGETrequest to the Reddit API on our chosen Subreddit. - The
func Get(url string) (resp *Response, err error)issues a GET to the specified URL. Whenerrisnil,respalways contains a non-nilresp.Body. Caller should closeresp.Bodywhen done reading from it. Therespis of type Response10. - We use the
deferfunction 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/httpdefines 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.Bodytype implementsio.Readerandos.Stdoutimplementsio.Writer.
Now you can run the program with the go tool:
$ cd $GOPATH/src/github.com/SatishTalim/redditnews
$ go run redditnews.go
When you run the program we are outputting (through os.Stdout) a huge blob of JSON that we received from the Golang Subreddit. Although we can actually see the Articles inside there, this is no good to us. We want to receive the Article’s Title, Author’s name, a Link to the Article, and we want to assess the value of the article, based on the Reddit link Score the Article has received.
- https://github.com/↩
- https://try.github.io/levels/1/challenges/1↩
- https://help.github.com/articles/set-up-git↩
- https://news.ycombinator.com/item?id=9315144↩
- https://addons.mozilla.org/en-us/firefox/addon/jsonview/↩
- http://golang.org/pkg/io/↩
- http://golang.org/pkg/log/↩
- http://golang.org/pkg/net/http/↩
- http://golang.org/pkg/os/↩
- http://golang.org/pkg/net/http/#Response↩
- http://golang.org/pkg/net/http/#pkg-constants↩
Appendix C - Build, deploy webapps to cloud
Complete Appendix A
I am assuming that you have completed Appendix A.
A basic Go web app
Our objective here is to build a web app in Go. In the following program, we shall start a simple service on our local machine. We will have the program running on some port number. When you access the local machine at http://localhost:port_no (which is the same as http://127.0.0.1:port_no), it will print a text message on the screen.
Make a new folder and cd to it as follows:
1 $ mkdir $GOPATH/src/github.com/SatishTalim/webapp
2 $ cd $GOPATH/src/github.com/SatishTalim/webapp
Note: Replace SatishTalim with your GitHub name.
In this folder, create a file named webapp.go, open it in your favorite editor, and add the following lines:
1 package main
2
3 import (
4 "fmt"
5 "log"
6 "net/http"
7 "os"
8 )
9
10 func main() {
11 http.HandleFunc("/", handler)
12
13 err := http.ListenAndServe(GetPort(), nil)
14 if err != nil {
15 log.Fatal("ListenAndServe: ", err)
16 }
17 }
18
19 func handler(w http.ResponseWriter, r *http.Request) {
20 fmt.Fprintf(w, "Hello. This is our first Go web program!")
21 }
22
23 // Get the Port from the environment so we can run on Heroku
24 func GetPort() string {
25 var port = os.Getenv("PORT")
26 // Set a default port if there is nothing in the environment
27 if port == "" {
28 port = "4747"
29 fmt.Println("INFO: No PORT environment variable detected, defaulting to " + port)
30 }
31 return ":" + port
32 }
- like all Go programs that need to be executed, our program has a package
main. - we import the fmt1 and net/http2 packages from the Go standard library.
- to work with some printing functions, we import the package
fmt. - package os3 provides a platform-independent interface to operating system functionality.
- for web related http functionality, we import the package http4. Any functions within that we refer as
http.function_name. - the first thing we need to do is tell our server what to do when someone comes to our homepage. So, we need to have a request handler at root (“/”). Within the
mainprogram, we redirect any incoming requests to thehandlerfunction. We do this by callinghttp.HandleFuncand 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 functionGetPortwhich 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
logimplements 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
handleris of the typehttp.HandlerFunc. It takes anhttp.ResponseWriterand anhttp.Requestas its arguments. - when a user connects, the program responds with a text that is sent back to the browser. The
http.ResponseWritervalue assembles the HTTP server’s response; by writing to it, we send data to the HTTP client. - an
http.Requestis a data structure that represents the client HTTP request. - all the parameters of a request can be received via the parameter
http.Requestin thehandler. You can get the URL, the input values and other details.
Now you can build and install that program with the go tool:
$ cd $GOPATH/src/github.com/SatishTalim/webapp
$ go install
You can now run the program by typing:
$ webapp
listening...
INFO: No PORT environment variable detected, defaulting to 4747
If you run this program and access the URL: http://localhost:47476 the program would present a page containing:
Hello. This is our first Go web program!
Great, it worked!
Static Sites with Go
A web application7 or web app is any application software that runs in a web browser or is created in a browser-supported programming language (such as the combination of JavaScript, HTML and CSS) and relies on a common web browser to render the application.
HTTP means HyperText Transfer Protocol. HTTP uses a simple conversation pattern: the client connects to the server, initiates the dialog by asking the server for something, the server then tries to provide the client with an answer (back). The basic principle of HTTP, and the Web in general, is that every resource (such as a web page) available on the Web has a distinct Uniform Resource Locator (URL), and that web clients can use HTTP verbs such as GET, POST, PUT, and DELETE to retrieve or otherwise manipulate those resources.
Our objective is to build a web app in Go to display a static site, which we call “Dosa Diner”.
Dosa is a fermented crepe or pancake made from rice batter and black lentils. This staple dish is widely popular in all southern Indian states Karnataka, Andhra Pradesh, Tamil Nadu and Kerala, as well as being popular in other countries like Sri Lanka, Malaysia and Singapore.
To build this static site, as mentioned before, you should have an understanding of basic web technologies (HTTP, HTML). In the following program, we shall start a simple service on our local machine. We will have the program running on port 3000. When you access the local machine at http://localhost:47478 (which is the same as http://127.0.0.1:47479), it will display a static site on the screen. Start by creating the folders to hold the project:
c:\go_projects
\---go
\---src
\---github.com
\---SatishTalim
\---dosasite
| dosasite.go
|
\---public
| index.html
|
+---images
| dosa.jpg
|
\---stylesheets
dosasite.css
We will write our Go code in the file dosasite.go and some sample HTML and CSS files in a public folder.
1 <!DOCTYPE html>
2 <html>
3
4 <head>
5 <title>Dosa Diner</title>
6 <meta charset="utf-8">
7 <link rel="stylesheet" href="stylesheets/dosasite.css">
8 </head>
9
10 <body>
11 <h1><img src="images/dosa.jpg" alt="Dosa Diner" />Dosa Diner</h1>
12
13 <h2>The Restaurant</h2>
14 <p>The Dosa Diner offers casual lunch and dinner fare in a hip atmosphere.
15 The menu changes regularly to highlight the freshest ingredients.</p>
16
17 <h2>Catering Services</h2>
18 <p>You have fun... we'll do the cooking. Dosa Diner Catering can handle events
19 from snacks for bridge club to elegant corporate fundraisers.</p>
20
21 <h2>Location and Hours</h2>
22 <p>Deccan Corner in Pune, India;
23 Monday through Thursday 11am to 9pm, Friday and Saturday, 11am to midnight.</p>
24 </body>
25
26 </html>
1 body {
2 background-color: #C2A7F2;
3 font-family: sans-serif;
4 }
5 h1 {
6 color: #2A1959;
7 border-bottom: 2px solid #2A1959;
8 }
9 h2 {
10 color: #474B94;
11 font-size: 1.2em;
12 }
13 h2, p {
14 margin-left: 120px;
15 }
Once the above files are created, the code we need to get up and running is quite compact:
1 package main
2
3 import (
4 "fmt"
5 "log"
6 "net/http"
7 "os"
8 )
9
10 func main() {
11 fs := http.FileServer(http.Dir("public"))
12 http.Handle("/", fs)
13
14 fmt.Println("Listening...")
15 err := http.ListenAndServe(GetPort(), nil)
16 if err != nil {
17 log.Fatal("ListenAndServe: ", err)
18 return
19 }
20 }
21
22 // Get the Port from the environment so we can run on Heroku (more of this later)
23 func GetPort() string {
24 var port = os.Getenv("PORT")
25 // Set a default port if there is nothing in the environment
26 if port == "" {
27 port = "4747"
28 fmt.Println("INFO: No PORT environment variable detected, defaulting to " + port)
29 }
30 return ":" + port
31 }
- like all Go programs that need to be executed, our program has a package
main. - we import the
fmt10 andnet/http11 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
publicfolder relative to our application, but you could use any other folder on your system (or indeed anything that implements theFileSysteminterface). - we use the Handle14 function to register it as the handler for all requests, and launch the server listening on port 4747.
Now you can run program with the go tool:
$ cd $GOPATH/src/github.com/SatishTalim/dosasite
$ go run dosasite.go
Now open http://localhost:4747/index.html15 in your browser. You should see the HTML page we have made.
Deploying Go Web Apps to Heroku
There are plenty of definitions for “cloud computing” online, and for the most part, they generally point to the same thing: taking applications and running them on infrastructure other than your own. Companies or individuals who offload or effectively “outsource” their hardware and/or applications are running those apps “in the cloud.”
Cloud Computing Service Levels
In the figure below, you can see how the analyst firm Gartner segregates cloud computing into three distinct classes of service.
SaaS
Let’s start at the highest level: software applications that are only available online fall into the “Software-as-a-Service” category, also known as “SaaS”. The simplest example to understand is e-mail. For personal e-mail, people typically select from a variety of free web-based e-mail servers such as Google’s Gmail, Yahoo!Mail, or Microsoft’s Hotmail, rather than setting up all of the above through their provider. Not only is it “free” (supported through advertising), but users are freed from any additional server maintenance. Because these applications run (and store their data online), users no longer need to worry about managing, saving, and backing up their files. Of course, now it becomes Google’s responsibility to ensure that your data is safe and secure. Other examples of SaaS include Salesforce, IBM’s NetSuite, and online games.
IaaS
On the opposite end of the spectrum, we have “Infrastructure-as-a-Service,” or “IaaS,” where you outsource the hardware. In such cases, it’s not just the computing power that you rent; it also includes power, cooling, and networking. Furthermore, it’s more than likely that you’ll need storage as well. Generally IaaS is this combination of compute and cloud storage.
When you choose to run your applications at this cloud service level, you’re responsible for everything on the stack that is required to operate above it. By this, we mean necessities such as the operating system followed by additional (yet optional services) like database servers, web servers, load-balancing, monitoring, reporting, logging, middleware, etc. Furthermore, you’re responsible for all hardware and software upgrades, patches, security fixes, and licensing, any of which can affect your application’s software stack in a major way.
PaaS
In the middle, we have “Platform-as-a-Service,” or “PaaS.” At this service level, the vendor takes care of the underlying infrastructure for you, giving you only a platform with which to (build and) host your application(s). Gone are the hardware concerns of IaaS, yet with PaaS, you control the application — it’s your code — unlike as the SaaS level where you’re dependent on the cloud software vendor. The only thing you have to worry about is your application itself.
Systems like Google App Engine, Salesforce’s Heroku and force.com, Microsoft Azure, and VMwares Cloud Foundry, all fall under the PaaS umbrella.
A number of Platform-as-a-Service (PaaS) providers16 allow you to use Go applications on their clouds.
Heroku17 is a new approach to deploying web applications18. Forget about servers; the fundamental unit is the app. Develop locally on your machine just like you always do. When you’re ready to deploy, use the Heroku client gem to create your application in their cloud, then deploy with a single git push. Heroku has full support for Go applications.
We shall soon see how we can deploy an app to Heroku.
Program webapp_h.go
Inside the folder $GOPATH/src/github.com/SatishTalim create the folder webapp_h.
Next inside the webapp_h folder, create a file named webapp_h.go, and give it the following contents:
1 package main
2
3 import (
4 "fmt"
5 "log"
6 "net/http"
7 "os"
8 )
9
10 func main() {
11 http.HandleFunc("/", handler)
12 fmt.Println("listening...")
13 err := http.ListenAndServe(GetPort(), nil)
14 if err != nil {
15 log.Fatal("ListenAndServe: ", err)
16 }
17 }
18
19 func handler(w http.ResponseWriter, r *http.Request) {
20 fmt.Fprintf(w, "Hello. This is our first Go web app on Heroku!")
21 }
22
23 // Get the Port from the environment so we can run on Heroku
24 func GetPort() string {
25 var port = os.Getenv("PORT")
26 // Set a default port if there is nothing in the environment
27 if port == "" {
28 port = "4747"
29 fmt.Println("INFO: No PORT environment variable detected, defaulting to " + port)
30 }
31 return ":" + port
32 }
This Go package responds to any request by sending a response containing the message:
Hello. This is our first Go web app for Heroku!
Create an account on Heroku
Please ensure that you are connected to the internet and then create an account on Heroku (obviously do this only once). If you don’t have one, then signup19. It’s free and instant. A free account can have upto 5 apps without registering your credit card.
Note: I just read on reddit that Heroku is moving away from free dynos20, which is sad.
Install the Heroku Toolbelt
The Heroku Toolbelt21 ensures that you have access to the Heroku command-line client, Foreman which is used to run applications locally, and the Git revision control system that is used to deploy sites to Heroku.
Once installed, you’ll have access to the heroku command from your command window. Log in using the email address and password you used when creating your Heroku account:
$ cd $GOPATH/src/github.com/SatishTalim/webapp_h
$ heroku login
Enter your Heroku credentials.
Email: golangchallenge@gmail.com
Password:
Could not find an existing public key.
Would you like to generate one? [Yn]
Generating new SSH public key.
Uploading ssh public key /Users/satish/.ssh/id_rsa.pub
Create a Procfile
Create a Procfile to tell Heroku what command to run for the web process in our app. The Procfile should be created in the folder $GOPATH/src/github.com/SatishTalim/webapp_h and contains:
web: webapp_h
Use Git
In order to deploy to Heroku we’ll need the app stored in Git. In the same folder i.e. $GOPATH/src/github.com/SatishTalim/webapp_h type:
$ git init
$ git add -A .
$ git commit -m "code"
Install Godep
The recommended way to manage Go package dependencies on Heroku is with Godep22, which helps build applications reproducibly by fixing their dependencies.
Before installing Godep ensure that you have Mercurial23 installed (check that you have an hg command) and have set the path environment variable for mercurial for eg. c:\Mercurial.
Now let us install Godep:
$ go get github.com/kr/godep
Now save your dependencies:
$ godep save
This will save a list of dependencies to the file Godeps/Godeps.json, and copy their source code into Godeps/_workspace.
Add these new files to git:
$ git add -A .
$ git commit -m "dependencies"
Now we’re ready to ship this to Heroku.
Heroku deploy
Create a new Heroku app, telling it to use the Go Heroku Buildpack24 to build your Go code:
$ heroku create -b https://github.com/kr/heroku-buildpack-go.git
Creating tranquil-bastion-1774... done, stack is cedar
BUILDPACK_URL=https://github.com/kr/heroku-buildpack-go.git
http://tranquil-bastion-1774.herokuapp.com/ | git@heroku.com:tranquil-bastion-1774.git
Git remote heroku added
Push the code to Heroku:
$ git push heroku master
Initializing repository, done.
Counting objects: 11, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (11/11), 1.29 KiB | 0 bytes/s, done.
Total 11 (delta 0), reused 0 (delta 0)
-----> Fetching custom git buildpack... done
-----> Go app detected
-----> Installing go1.2... done
-----> Running: godep go install -tags heroku ./...
-----> Discovering process types
Procfile declares types -> web
-----> Compressing... done, 1.7MB
-----> Launching... done, v4
http://pumpkin-cupcake-1256.herokuapp.com deployed to Heroku
To git@heroku.com:pumpkin-cupcake-1256.git
* [new branch] master -> master
Your app should be up and running. Visit it with:
$ heroku open
Opening pumpkin-cupcake-1256... done
That’s it - you now have a running Go app on Heroku!
Here’s the URL again: http://pumpkin-cupcake-1256.herokuapp.com/25.
A brief note on Heroku
Troubleshooting
A number of bugs can potentially be encountered in the process of deploying a Go application to Heroku. Many times, a bug can be traced to a misunderstanding of Heroku’s architecture and how it works.
Heroku’s Architecture
Overall, Heroku applications are expected to follow the “process model”26 architecture. The process model allows applications, and web applications in particular, to be easily scaled out and managed with little administrative overhead. In exchange, the architecture requires applications to conform to certain rules and restrictions.
There are two fundamental parts to a Heroku application: Dynos, which follow the proccess model; and Add-ons, which are provided through third-party integrations.
Dynos
The main unit of a Heroku application is the Dyno.
Dynos are ephemeral servers which run a single process. Each process started by a dyno must be assigned a “process type”. A dyno’s type is defined by the starting process type.
Even though a dyno can only start a single process, once started that process may spawn additional processes within the dyno within limits27.
Keep in mind, dynos do not retain data. Data does not persist between Dyno restarts and there are no shared volumes between dynos. Practically speaking, you cannot retain any data on a dyno. Add-ons28 should be used for persistence and are explained below.
There are three types29 of Dynos tailored for different workloads: web dynos, worker dynos, and one-off dynos.
Only web dynos receive HTTP traffic via the Heroku router. Heroku assigns a single $PORT environment variable to all dynos running a web process type. All inbound HTTP traffic to a web process goes through the single assigned $PORT variable. The Heroku router attaches headers30 to HTTP requests to describe the original request as received by Heroku.
Web Dynos also have the following restrictions not discussed above:
- They allow 50 active requests per web dyno with a 50 request queue.
- They must connect to the Heroku provided $PORT environment variable within 60 seconds31 or the dyno will automatically be shut down.
Add-ons
Persistence and other functionality needed by applications which cannot be provided by Dynos are available through Add-ons32. Many add-ons are integrated with Dynos through environment variables.
Slugs
In addition to the process described above, Go applications can also be cross-compiled into a binary and deployed to Heroku through the use of a custom slug. Slugs are containers (similar to Docker images) which hold everything needed to run a program on Heroku’s infrastructure. To deploy a Go application this way, a slug needs to be created, published, and released on Heroku. Heroku has published an article specifically about deploying Go slugs33.
The address used in the exercise above is that of a landmark, historic home and office of the United States president.
Exercise 18 Solution:
Exercise 19 Solution:
License
This work is licensed under the Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit here36.
All example code used in this book is hereby put in the public domain.
©Satish Talim - 2015
- http://golang.org/pkg/fmt/↩
- http://golang.org/pkg/net/http/↩
- http://golang.org/pkg/os/↩
- http://golang.org/pkg/net/http/↩
- http://golang.org/pkg/log/↩
- http://localhost:4747↩
- http://en.wikipedia.org/wiki/Web_application↩
- http://localhost:4747↩
- http://127.0.0.1:4747↩
- http://golang.org/pkg/fmt/↩
- http://golang.org/pkg/net/http/↩
- http://golang.org/pkg/net/http/#FileServer↩
- http://golang.org/pkg/net/http/#FileSystem↩
- http://golang.org/pkg/net/http/#Handle↩
- http://localhost:4747/index.html↩
- https://code.google.com/p/go-wiki/wiki/ProviderIntegration↩
- http://heroku.com/↩
- http://mmcgrana.github.io/2012/09/getting-started-with-go-on-heroku.html↩
- http://heroku.com/signup↩
- https://news.ycombinator.com/item?id=9315144↩
- https://toolbelt.heroku.com↩
- https://github.com/kr/godep↩
- http://mercurial.selenic.com/downloads/↩
- https://github.com/kr/heroku-buildpack-go↩
- http://pumpkin-cupcake-1256.herokuapp.com/↩
- https://devcenter.heroku.com/articles/process-model↩
- https://devcenter.heroku.com/articles/dynos#process-thread-limits↩
- https://addons.heroku.com/↩
- https://devcenter.heroku.com/articles/dynos#types-of-dynos↩
- https://devcenter.heroku.com/articles/http-routing#heroku-headers↩
- https://devcenter.heroku.com/articles/dynos#web-dynos↩
- https://addons.heroku.com/↩
- https://devcenter.heroku.com/articles/platform-api-deploying-slugs#go↩
- https://developers.google.com/maps/documentation/geocoding/↩
- https://developers.google.com/maps/documentation/streetview/index↩
- http://creativecommons.org/licenses/by-nc-sa/3.0/↩