The Go Programming Language
- Go is a compiled programming language in the tradition of C and C++, with static typing, garbage collection, and unique language features enabling concurrent programming
- Latest Release: 1.8rc3 (1.8 will be out soon)
- Developed internally by Google to solve the kind of problems unique to Google (ie, high scale services/systems)
- Designers/developers of Go have deep ties to C/Unix (Ken Thompson, Rob Pike, Robert Griesemer, et al)
Hello World in Go
package mainimport "fmt"
func main() { fmt.Println("hello world")}Go Language and Runtime Feature Overview
- Small and powerful standard library
- Garbage collected
- Statically compile (or cross-compile!) and deploy almost anywhere
- Super-fast compiles and single binary deploys
- Language/standard library are UTF-8 native
- Design and behavior of language/standard library is opinionated
- Since v1.5, compiler toolchain is written in Go
- Built in unit testing
- Easy integration with C code/libraries
- Less-is-more!
Installing Go
- Download binaries from golang.org/dl
- Or, install from source:
#!/bin/bash
sudo -scd /usr/local/
export GOROOT_BOOTSTRAP=/usr/local/go-1.7.4
git clone https://go.googlesource.com/gocd go/src && git checkout go1.8rc3./all.bash
export PATH=$PATH:/usr/local/go/binIDEs
- VSCode - is a new, but strong, cross-platform IDE built by Microsoft, and works well with Go!
- JetBrains Gogland - new, upcoming JetBrains IDE for Go
- Plugins available for most other IDEs/editors — Sublime, IntelliJ, etc
Installing VS Code with Go
- Download Installer
- Open a Go file
- Install the recommended Go extension
- Write code!

Run/Build/Install command line example:
-
Run:
$ go run hello.go -
Build and Execute:
Terminal window $ go build hello.go$ lshello hello.go$ ./hellohello world -
Install (puts hello in
$GOPATH/bin/):$ go install
Packages and go get
-
Go code is grouped in “packages”: a directory containing one or more .go files
-
Packages are retrievable via go get:
$ go get -u github.com/knq/baseconv -
The above will fetch the Go Git repository and store it in $GOPATH/src/$REPO:
Terminal window $ cd $GOPATH/src/github.com/knq/baseconv$ lsbaseconv.go baseconv_test.go coverage.out coverage.sh example LICENSE old README.md -
A package may have any number of sub directories each of which is its own package, ie:
github.com/knq/baseconv // would import "baseconv"github.com/knq/baseconv/subpackage // would import "subpackage"
Package Imports, and Visibility
-
Packages (ie, libraries) can be imported into the current package:
import "github.com/knq/baseconv" -
Only func’s and type’s defined in that package beginning with a capital letter are visible when imported:
func doSomething() {} // not visible to other packagesfunc DoSomething() {} // visible to other packages -
For example:
import ("fmt")fmt.Println("foobar")fmt.print("hello") // compiler error -
When in doubt, start the name with a capital
-
You can define an import alias for a package:
import (j "github.com/knq/baseconv")// baseconv's exported funcs/types are now available under 'j':j.Decode62() -
Some packages need to be imported for their side-effect:
import (// imports the postgres database driver package_ "github.com/lib/pq")
Building, Testing, and Installing a Go package from command line
$ cd $GOPATH/src/github.com/knq/baseconv/$ go build$ go test -v=== RUN TestErrors--- PASS: TestErrors (0.00s)=== RUN TestConvert--- PASS: TestConvert (0.00s)=== RUN TestEncodeDecode--- PASS: TestEncodeDecode (0.00s)PASSok github.com/knq/baseconv 0.002s$ go installSome Notes on Go’s Syntax/Design
- Go designers have purposefully omitted many common features in other languages in the interest of simplicity and readability above almost all else
- If it can already be done through some other feature available to the language, then there is not a need for a specific language feature
Quick Syntax Primer
- Go is C-like, but:
- No semicolons — every line break implies a semicolon
- Variable names come before the type
- Braces are required for control statements (for, if, switch, …)
- Parentheses are not used in control statements
- Typing is implicit in assignments
- Unused import or variable is a compiler error
- Trailing commas are required
- Standard syntax formatting that is applied automatically with gofmt
C vs Go, a simple comparison
- Example of printing all command line arguments in C and Go:
#include <stdio.h>
int main(int argc, char **argv) { for (int i = 0; i < argc; i++) { printf(">> arg %d: %s\n", i, argv[i]); } return 0;}package main
import ( "fmt" "os")
func main() { for i, a := range os.Args { fmt.Printf(">> arg %d: %s\n", i, a) }}Standard Types (builtin)
var b bool = falsevar s string = ""var b byte = 0 // alias for uint8
// -1 0int uintint8 uint8int16 uint16int32 uint32int64 uint64
// 0.0 1.0i ...float32 complex64float64 complex128
// 'c'rune // alias for int32
uintptr // an integer type that is large enough to hold the bit pattern of any pointer.Expressions and Assignments
-
Variable names can include any UTF-8 code point:
var a = "" // variable "a" is string value ""var 世界 = 15 // variable "世界" is int value 15var f bool // variable "f" is bool value falseb := "awesome" // variable "b" is string value "awesome"b = "different string" // variable "b" is assigned value "different string" -
Expressions:
a := b * c // a is assigned the value of b * c -
Supports multiple return values from functions:
func someFunc() (int, int) { return 7, 10 }a, b := someFunc() // a = 7, b = 10 -
Special typeless variable _ can be used as placeholder in an assignment:
a, b, _ = anotherFunc() // the third return value of anotherFunc will be ignored
Expressions and Operators
-
Usual operators:

-
Note: operators are only are available as a single expression (cannot be inlined), ie:
// validi++j--// not validj[i++] -
Otherwise, operators work as expected:
j *= 10i = i + 15
Constants
-
Go declares constants using the keyword “const”:
const (MyString string = "foobar") -
A const can be any expression:
const (// typed constMyConst string = "hello"// not typedMyOtherConst = 0) -
iota is special value for incrementing constants:
const (MyConstA = iota // 0MyConstB // 1MyConstC // 2)
Slices, Maps, Arrays
-
There are fixed-length arrays, but rarely used:
var a [8]byte -
A slice provides a dynamic list of any type:
var a = []int{15, 20, 9}for i := range a {fmt.Printf(">> %d\n", a[i])} -
Maps (dictionaries/hashes in other languages) provides a robust map of key to value pairs for any type:
var a = map[string]int{"foo": 10,"bar": 15,}for k, v := range a {fmt.Printf(">> %s: %d\n", k, v)}
make and new
-
make is used to allocate either a slice, map or channel with a size:
a := make([]string, 15) // a has type '[]string' and initial length of 15b := make(map[string]interface{}, 0) // b has type 'map[string]interface{}'c := make(chan *Point) // c has type 'chan *Point' -
new allocates a new instance of the type and returns a pointer to the allocated instance:
b := new(Point) // b has type '*Point'p := &Point{} // more or less the same as new(Point)i := new(int) // i has type '*int'
append, len, and reslicing
-
append is used to append to a slice
b := []string{"foo", "bar"}b = append(b, "another string") // b is now []string{"foo", "bar", "another string"} -
len provides the length for slices, maps, or strings:
a := map[string]int{0, 12}b := []int{14, 13, 3}len(a) // 2len(b) // 3len("hello") // 5 -
Any slice or array can be resliced:
a := []string{"foo", "bar", "make", "new"}b := a[:1] // slice a from 0 to 1 -- b is []string{"foo"}c := a[1:3] // slice a from 1 to 3 -- c is []string{"bar", "make"}d := a[1:] // slice a from 1 to len(a) -- d is []string{"bar", "make", "new"}
func
-
Functions are declared with func, and the return type follows the parameter list:
func empty() {} // no return valuefunc doNothing(a string, c int) error { return nil } // returns error -
A func can be assigned to a variable:
func someFuncName() error { return nil }a := someFuncName // a has type 'func() error' -
A func can also be declared inline:
func main() {g := func() {doSomething()}g()func(b int) {fmt.Printf("%d\n", b)}(10)}
Control Statements
-
if/else if/else:
if cond {expr} else if cond {expr} else {expr} -
switch/case/default:
switch v {case "e":// somethingdefault:// default} -
switch does not require break statements and cases do not automatically fallthrough
Control Statements
-
switch as replacement for complex if/else chains:
switch {case i == 0 && err != nil:// somethingcase i == 6:// somethingcase j == 9:// somethingdefault:// default} -
select is like switch, but waits on a channel:
select {case a := <-c:// read a from channel ccase <-time.After(15*time.Second):// a 'timeout' after 15 seconds}
Control Statements
-
In Go, “while” is spelled “for” — the only provided loop is for:
for cond {}for {if !cond {break}}loop:for i := 0; i < len(someSlice); i++ {for {if a == 15 {break loop}}}for key, value := range someSlice {}
Variadic parameters
-
func can have variable args (variadic)
-
Must be last parameter
-
Special symbol … to indicate expansion:
func doSomething(prefix string, intList ...int) {for m, n := range intList {fmt.Printf("> %s (%d): %d\n", prefix, m, n)}} -
Can be used also in append statements:
strList := []string{"bar"}j := append([]string{"foo"}, strList...) // j is []string{"foo", "bar"}
Type Declaration
-
No classes or objects
-
struct provides compound (“structured”) types:
type Point struct {X, Y float64} -
and interface defines a set of func’s:
type Reader interface {Read([]byte) (int, error)} -
Can create a type copy for any other type:
type MyUnsignedInteger uint32type MyPoint Pointtype MyReader Readertype MyFunc func(string) error
Notes on Types
-
Same export / visibility rules apply for struct members:
type Point struct {X, Y float64j int} -
Only Go code in the same package as Point can see Point.j
-
Type conversions (casts) are always explicit!
type MyStr stringa := MyStr("blah") // a is of type MyStr and has value "blah"var b string = a // compiler errorvar c string = string(a) // c is of type string and has value "blah"
Type Receivers
-
A func can be given a receiver:
type MyType struct {MyValue int}func (mt MyType) AddOne() int {return mt.MyValue+1}type MyString stringfunc (ms MyString) String() string {return string(ms)} -
A func with a pointer receiver, allows the func to modify the value of the receiver:
// Increment increments MyType's MyValue and returns the result.func (mt *MyType) Increment() int {mt.MyValue++return mt.MyValue}
About interface
-
Unlike Java or other object-oriented languages, there is no need to explicitly declare a type as having an interface:
type Reader interface {Read([]byte) (int, error)}type MyReader string// Read satisfies the Reader interface.func (mr MyReader) Read([]byte) (int, error) { /* ... */ }// DoSomething does something with a Reader.func DoSomething(r Reader) { /* ... */ }func main() {s := MyReader("hello")DoSomething(s)}
Pointers, Interfaces, and nil
-
Go pointers are similar to pointers in C/C++ (address to a variable), but there is no pointer math in Go!
-
The . operator is used for both pointer dereference and for accessing a member variable:
type Point { X, Y int }a := Point{10, 20} // a has type 'Point' with value {X: 10, Y: 20}b := &Point{X: 30, Y: 40} // b has type '*Point' and **points** to value {X: 30, Y: 40}*b = Point{Y: 80} // b now points to value {X: 0, Y: 80}// . is used to access struct members for both a and b:fmt.Printf("(%d %d) (%d %d)", a.X, a.Y, b.X, b.Y) // prints "(10 20) (0 80)" -
Any type pointer or interface can be assigned the nil value:
type Reader interface{}var a *Point = nilvar b MyReader = nil
Goroutines and Channels
-
Killer features of Go that provides lightweight concurrency in any Go application
-
Any func in Go can be a goroutine:
func main() {for i := 0; i < 10; i++ {go func(z int) {fmt.Printf(">> %d\n", z)}(i)}time.Sleep(1*time.Second)} -
Channels are a unique feature in Go that provides type safe memory sharing
c := make(chan int)c <- 10 // write 10 to cj := <-c // read int from c// channels can be read or write only:var c <-chan int // read only chanvar d chan<- int // write only chan
Goroutine and Channel example
package main
import ( "fmt" "sync" "time")
func main() { var wg sync.WaitGroup
c := make(chan int) for i := 0; i < 10; i++ { wg.Add(1) go func(z int) { defer wg.Done() time.Sleep(1 * time.Second) c <- z }(i) }
wg.Add(1) go func() { defer wg.Done() for { select { case z := <-c: fmt.Printf(">> z: %d\n", z) case <-time.After(5 * time.Second): return } } }()
wg.Wait() fmt.Println("done")}Handling Errors
-
No try/case equivalent
-
Breaking flow should be done by checking for an error:
func MyFunc() error {return errors.New("error encountered")}err := MyFunc()if err != nil {// handle error} -
Utility func in the standard library fmt package:
fmt.Errorf("encountered: %d at %s", line, str) // returns a error
Error Types
-
error is a special Go interface:
interface error {Error() string} -
Any type can be an error by satisfying the error interface:
type MyError struct {}func (me *MyError) Error() string {return "my error"}func doSomething() error {return &MyError{}}
defer
-
defer is a great feature of Go that allows executes the func when the parent func returns:
func doSomething() error {db, err := sql.Open(/* ... */)if err != nil {return err}defer db.Close()err = db.Exec("DELETE ...")/* ... */}
panic and recover
-
panic allows immediate halt of the current goroutine:
panic("some error") -
recover() can only be called in defer’d func’s, but allows recovery after a panic:
func myFunc() {defer func() {if e := recover(); e != nil {log.Printf("run time panic: %v", e)}}()panic("my panic")} -
Note: panic’s should not be used unless absolutely necessary.
Quick Overview of the Standard Library
```goimport ( "fmt" // string formatting "strings" // string manipulation "strconv" // string conversion to standard types "io" // system input/output package "sync" // synchronization primitives "time" // robust time handling/formatting "net/http" // http package supporting http servers and clients
"database/sql" // standardized sql interface "encoding/json" // json encoding/decoding (also packages for xml, csv, etc)
// template libraries for text and html "text/template" "html/template"
// cryptographic libs "crypto/rsa" "crypto/elliptic"
"reflect" // go runtime introspection / reflection "regexp" // regular expressions)// And many many many more!```What Go doesn’t have (and why this is a good thing)
- Generics
- Implicit comparisons
- Overloading / Inheritance
- Objects
- Ternary operator (?:)
- Miscellany data structures (vector, set, etc)
- Package manager
Working Example
package main
import ( "encoding/json" "fmt" "io/ioutil" "net/http" "os")
func main() { // read from web res, err := http.Get("http://ifconfig.co/json") if err != nil { fmt.Fprintf(os.Stderr, "error: %v", err) os.Exit(1) } defer res.Body.Close()
// read the body body, err := ioutil.ReadAll(res.Body) if err != nil { fmt.Fprintf(os.Stderr, "error: %v", err) os.Exit(1) }
// decode json var vals map[string]interface{} err = json.Unmarshal(body, &vals) if err != nil { fmt.Fprintf(os.Stderr, "error: %v", err) os.Exit(1) }
for k, v := range vals { fmt.Fprintf(os.Stdout, "%s: %v\n", k, v) }}