go-crashcourse



go-crashcourse

1 6


go-crashcourse

This project is a 1 hour presentation of the fundamentals of Go, written in Reveal.js

On Github nii236 / go-crashcourse

Go Crash Course

A quick 1 hour workshop on the basics of Go

John Nguyen

About Me

  • Studied Mechatronics Engineering at UWA
  • Worked in Mechanical and Process Controls Engineering
  • Started working at The Frontier Group in 2015
  • Have worked with python, ruby/rails, nodejs, C#

About Go

Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.

Notable Companies Using Go

  • Google / Facebook / Yahoo
  • Rackspace / Digital Ocean / Heroku
  • Shopify / Booking.com / Medium
  • IBM / Comcast / Canonical
  • Square
  • SendGrid / Bitly
  • Docker
  • Dropbox

Why Go?

  • Simple
  • Maintainable
  • Scales easily
  • Easily generated documentation
  • Fast
  • Robust tooling
  • Single, static binary
  • Fast compile times
  • Cross platform
  • Good community
  • Language level concurrency (no callback hell)
  • Useful error messages

Why not Go?

  • No generics
  • No operator overloading
  • Inelegant
  • Ugly syntax
  • Poor dependency management
  • Garbage collector can not be modified easily
  • Verbose error handling

Installation

OSX

brew update
brew install go

Linux

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

Web

Visit https://play.golang.org/.

Paths

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

Tooling

Go comes with its own tool, called go. It manages many things, and any missing functionality can be covered with third party tools.

Go tool

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

Documentation

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

Third Party Tools

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

Using Go

The commands go get, go build, go run, and go test will probably be your most commonly used commands.

Cross compilation

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

Editors

Program Structure

Go has a very opinionated structure. There are tools to get around this but it is a better idea to follow best practices.

GOPATH

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

Packages

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"

Vendoring

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):

Exported and Unexported Identifiers

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() {}

Variables

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)

Types

Go's type system is very simplistic and not easily extensible.

Data Types

  • Integers
  • Floats
  • Complex Numbers
  • Booleans
  • Strings
  • Constants

Composite Types

  • Arrays
  • Slices
  • Maps
  • Struct
  • JSON
  • Text and HTML Templates

Program Flow

Link

Methods to control the program flow are similar to C based languages.

Conditionals

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
}

Select

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)
    }
}

Switch

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")
}

Loops

There is only a single reserved word for loops.

for initialization; condition; post {
    // Vanilla loop
}

for condition {
    // While loop
}

for {
    // Infinite loop
}

Range

Iterable objects can also be ranged over.

a := [1, 2, 3]
for _, v := range a {
    fmt.Println(v)
}

Structs

Link

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.

Struct Example

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
}

Instantiating Structs

You refer to struct objects with their memory address when assigning to a variable

func main() {
    p := &Person{"John", 29}
    fmt.Println(p.Name)
}

Embedding Structs

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
}

Attaching Methods

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"
}

Procedures

Go allows both functions and methods with collections of functions passed around as interfaces.

Functions

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) {
    ...
}

Anonymous Functions

Link

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.

Methods

Go also allows you to attach methods to structs

func (s *SomeStruct) mutableMethod(a int) int {
    ...
}

func (s SomeStruct) immutableMethod(a int) int {
    ...
}

Interfaces

Link

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

Hello World

We're going to start with a simple hello world program.

Hello World Example

Link

The below code will get you going:

package main

import "fmt"

func main() {
    fmt.Println("Hello, 世界")
}

To run it:

$ go run main.go
Hello, 世界

Command Line Tools

Let's have a look at parsing command line flags. Go is quite good at building command line tools.

Command Line Tool Example

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"

Concurrency

Go has two main forms of managing concurrency. Goroutines and Channels.

Go Routines

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.

Go routine example

Link

// 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

Channels are typed conduits for synchronization and communication between go routines.

Go channel example

Link

// Channels is an example of Go channels.
func Channels() {
    messages := make(chan string)
    go func() { messages <- "ping" }()
    msg := <-messages
    fmt.Println(msg)
}

Web Client and Server

Go has a very solid standard library and its common in the community to focus on solving most problems with the standard library.

Client

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)
}

Server

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.

Server with Templated HTML

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.

Route Handlers

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))
}

Templating

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

Testing

Go has a robust standard tooling for table based XUnit type tests. Additional third party libraries are available for more BDD style assertions.

Testing Example

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

The End.

Go Crash Course A quick 1 hour workshop on the basics of Go John Nguyen