On Github nii236 / go-crashcourse
A quick 1 hour workshop on the basics of Go
John Nguyen
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.
brew update brew install go
Download binary from https://golang.org/dl/
wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz sudo tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
Visit https://play.golang.org/.
Add the following to your .profile. This will add the binaries to your PATH and also setup your GOPATH environment
export PATH=$PATH:/usr/local/go/bin/:$HOME/go/bin export GOPATH=$HOME/go
Go comes with its own tool, called go. It manages many things, and any missing functionality can be covered with third party tools.
The go tool can handle fetching dependencies, running tests, installing binaries, running programs, linting, code formatting, and many others!
build compile packages and dependencies clean remove object files env print Go environment information fix run go tool fix on packages fmt run gofmt on package sources get download and install packages and dependencies install compile and install packages and dependencies list list packages run compile and run Go program test test packages tool run specified go tool version print Go version vet run go tool vet on packages
The Go project cares a lot about documentation, and golint enforces clean code that can be parsed for generation of documentation.
The godoc tool can parse code to render documentation to stdout.
$ godoc fmt Formatter use 'godoc cmd/fmt' for documentation on the fmt command type Formatter interface { Format(f State, c rune) } Formatter is the interface implemented by values with a custom formatter. The implementation of Format may call Sprint(f) or Fprint(f) etc. to generate its output.
The godoc tool also contains a webserver!
$ godoc -http=":8082" $ open http://localhost:8082
The third party tooling is very good considering how young Go is. Many of the tools are built in Go, and write to stdout so can be easily parsed and plugged into any editors easily.
A short list of essential third party tools are:
glide Dependency Management gocode Allows package inspection for autocompletion golint Linting for go source code godef Go to definition
The commands go get, go build, go run, and go test will probably be your most commonly used commands.
To cross compile to a different system architecture, prefix with the relevant environment variables.
Cross compiling for Windows from OSX:
GOOS=windows GOARCH=amd64 go build main.go
Go has a very opinionated structure. There are tools to get around this but it is a better idea to follow best practices.
The GOPATH is where everything is stored. The GOPATH has the following tree:
$GOPATH ├── bin └── gocode └── goimports └── goreturns ├── pkg └── darwin_amd64 └── github.com └── nii236 └── repo-name └── src └── github.com └── nii236 └── repo-name
These are basically libraries or modules in other languages and is namespaced. Exported identifiers in each package allows users to consume the packages as intended.
Packages are fetched with go get and are imported with a fully qualified name and will fetch the HEAD everytime.
import "github.com/exampleUser/exampleRepo"
Since Go 1.5, there has been support for vendoring and dependency management (which allows us to control which versions of packages we fetch).
There have been many third party tools created for this, as the creators of Go do not see it as necessary (as they live in Google's monorepo environment):
If the first letter of a function is a capital, then it is considered an exported identifier and can be accessed from outside the package.
// ExportedFunction is a function that is // accessible outside of the package func ExportedFunction() {}
If the first letter is lowercase instead, it will be considered private.
func unexportedFunction() {}
There are a few ways of declaring variables.
var a, b, c int var i, j, k = true, 2.3, "four" f, err := os.Open(name)
Go's type system is very simplistic and not easily extensible.
Methods to control the program flow are similar to C based languages.
The if statement behaves as expected.
if condition { // Do something }
You can also assign temporary variables.
if a := something; condition { // Assign a variable or run a function then do something }
The select statement lets a goroutine wait on multiple communication operations.
for i := 0; i < 2; i++ { select { case msg1 := <- c1: fmt.Println("received", msg1) case msg2 := <- c2: fmt.Println("received", msg2) } }
These statements express conditionals across many branches.
i := 2 fmt.Print("write ", i, " as ") switch i { case 1: fmt.Println("one") case 2: fmt.Println("two") case 3: fmt.Println("three") }
There is only a single reserved word for loops.
for initialization; condition; post { // Vanilla loop } for condition { // While loop } for { // Infinite loop }
Iterable objects can also be ranged over.
a := [1, 2, 3] for _, v := range a { fmt.Println(v) }
These are a very important part of Go and are worth focusing on.
Structs are typed collections of fields.
Structs are also what enables Go's composition based object oriented programming.
Here is a struct declaration of a Person containing a Name of type string, and an Age of type int.
type Person struct { Name string Age int }
You refer to struct objects with their memory address when assigning to a variable
func main() { p := &Person{"John", 29} fmt.Println(p.Name) }
Go does not provide the typical, type-driven notion of subclassing, but it does have the ability to “borrow” pieces of an implementation by embedding types within a struct or interface.
type Person struct { Name string Age int } type Developer struct { Person Company string } type Student struct { Person University string }
Use struct pointers only if you need to mutate the object.
func (p Person) sayHello() { fmt.Println("Hello, I am", p.Name) } func (d Developer) sayHello() { fmt.Println("BeepBoop I am", d.Name) } func (d *Developer) changeName(in string) { d.Name = in }
Run the program.
func main() { p := &Person{"Andy", 32} d := &Developer{Person{"John", 29}, "TFG"} p.sayHello() // Prints "Hello, I am Andy" d.sayHello() // Prints "BeepBoop I am John" d.changeName("Johnny") d.sayHello() // Prints "BeepBoop I am Johnny" }
Go allows both functions and methods with collections of functions passed around as interfaces.
Go supports first class functions that do not need to be attached or instantiated from a class/object
Functions in Go can have multiple return values
func exampleFunction(varName string) ([]int, error) { ... }
Go supports anonymous functions.
They can also be used as a return value given the same function signature.
// AnonymousFunc is an anonymous function type // that can be used as a return value. type AnonymousFunc func() []int // Parentfunc simply returns AnonymousFunc. func Parentfunc() AnonymousFunc { return func() []int { return []int{1, 2} } }
This can be useful for creating wrappers around functions such as building middleware for HTTP handlers.
Go also allows you to attach methods to structs
func (s *SomeStruct) mutableMethod(a int) int { ... } func (s SomeStruct) immutableMethod(a int) int { ... }
Interfaces in Go provide a way to specify the behavior of an object: if something can do this, then it can be used here.
Interfaces are satisfied implicitly.
type ExampleInterface interface { methodOne(in string) string methodTwo(in []int) (int, error) } type First struct { RandomInfo string} type Second struct { OtherInfo int} func (s *First) methodOne(a int) string {} func (s *First) methodTwo(a int) (int, error) func (s *Second) methodOne(a int) string {} func (s *Second) methodTwo(a int) (int, error) {} func satisfied (ex ExampleInterface) { // Either first or second struct can be used! }
We're going to start with a simple hello world program.
The below code will get you going:
package main import "fmt" func main() { fmt.Println("Hello, 世界") }
To run it:
$ go run main.go Hello, 世界
Let's have a look at parsing command line flags. Go is quite good at building command line tools.
func main() { var cmd string flag.StringVar(&cmd, "cmd", cmd, `cmd can be either "hello" or "bye"`) flag.Parse() switch cmd { case "hello": fmt.Println("Hello!") case "bye": fmt.Println("Bye!") default: flag.Usage() } }
Lets build it first then run it.
$ go build main.go $ ./go-crashcourse Usage of ./go-crashcourse: -cmd string cmd can be either "hello" or "bye"
Go has two main forms of managing concurrency. Goroutines and Channels.
Go routines are light weight threads that are managed by the Go runtime. To run a function in a new go routine, just put "go" before the function call.
// Routines is an example of Go routines. func Routines() { go say("let's go!", 3*time.Second) go say("ho!", 2*time.Second) go say("hey!", 1*time.Second) time.Sleep(4 * time.Second) fmt.Println("Finished") } func say(text string, delay time.Duration) { time.Sleep(delay) fmt.Println(text) }
Channels are typed conduits for synchronization and communication between go routines.
// Channels is an example of Go channels. func Channels() { messages := make(chan string) go func() { messages <- "ping" }() msg := <-messages fmt.Println(msg) }
Go has a very solid standard library and its common in the community to focus on solving most problems with the standard library.
The net/http package provides an HTTP client.
func main() { r, err := http.Get("http://www.golang.org/") if err != nil { log.Fatal(err) } if r.StatusCode != http.StatusOK { log.Fatal(r.Status) } io.Copy(os.Stdout, r.Body) }
The net/http package also provides an HTTP server.
func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello, web") }) fmt.Println("Starting server") err := http.ListenAndServe("localhost:8080", nil) if err != nil { log.Fatal(err) } }
This is a high-performance, DoS-hardened and production-ready web server. It serves dl.google.com.
We will need to fetch our first dependency for this code.
go get github.com/gorilla/mux
The code below creates a webserver that handles only a single route, /, unlike before.
import "github.com/gorilla/mux" // PartTwo runs a web server with templating func PartTwo() { rt := mux.NewRouter().StrictSlash(true) // Index is described in the slide below rt.HandleFunc("/", Index) log.Println("PART TWO: Starting server of localhost:8081") log.Fatal(http.ListenAndServe(":8081", rt)) }
The route handler Index will be handled next.
Index will be executed when the route / is hit.
//Index is the handler for root path func Index(w http.ResponseWriter, r *http.Request) { pd := PageData{ Title: "Index Page", Body: "This is the body of the page", } tmpl, err := htmlTemplate(pd) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } w.Write([]byte(tmpl)) }
HTML templating is a powerful feature of Go and lets you render data-driven HTML.
As of Go 1.6, blocks are introduced which let you build templates on other templates.
func htmlTemplate(pd PageData) (string, error) { html := `<HTML> <head> <title> {{.Title}} </title> </head> <body> {{.Body}} </body> ` tmpl, err := template.New("index").Parse(html) if err != nil { return "", err } var out bytes.Buffer if err := tmpl.Execute(&out, pd); err != nil { return "", err } return out.String(), nil }
Now we can update our CLI tool and execute our server.
$ go build ./go-crashcourse -cmd serve2 Running server! 2016/03/04 10:57:19 PART TWO: Starting server of localhost:8081
Next we visit localhost:8081
open http://localhost:8081
Go has a robust standard tooling for table based XUnit type tests. Additional third party libraries are available for more BDD style assertions.
First we will build the test suite.
package add_test import ( "testing" "github.com/nii236/gotraining/testing" ) var tests = []struct { in []int out int }{ {[]int{1, 2}, 3}, {[]int{-1, 2}, 1}, {[]int{1, 0}, 1}, }
Next we will write the test itself.
func TestFunc(t *testing.T) { for _, tt := range tests { got := add.Do(tt.in[0], tt.in[1]) expected := tt.out if got != tt.out { t.Errorf("Error! Got %d, expected %d", got, expected) } } }
Now we will write the code that satisfies the test.
package add //Do runs a test func given two integers func Do(a int, b int) int { return a + b }
And finally we can run the test and see how we go!
$ go test ./... ? github.com/nii236/gotraining/go-crashcourse [no test files] ? github.com/nii236/gotraining/go-crashcourse/concurrency [no test files] ? github.com/nii236/gotraining/go-crashcourse/hello [no test files] ? github.com/nii236/gotraining/go-crashcourse/serve [no test files] ? github.com/nii236/gotraining/go-crashcourse/structs [no test files] ok github.com/nii236/gotraining/go-crashcourse/testing 0.005s