William Liu

Golang


##Summary

Go is an open source programming language that focuses on concurrency (i.e. instead of sequentially/one after another).

###Why Go?

There’s sequential computing, which runs one command first, then another command as soon as the first is completed. Concurrent computing is different in that it has many computations executing during overlapping time periods. Concurrent computing is commonly confused with Parallel computing, but it doesn’t mean the same thing.

##Install

##Go Tools

Go comes with a bunch of tools for managing Go code. You can access by typing in go. Some of these tools can you help build (compile code), install packages and dependences and run programs.

###Go Get

You can get source code by using go get. For example, using go get golang.org/x/tour/gotour.

###Go Install

You compile and install packages and dependencies using go install.

###Workspace Directory Tree

So now you have some code, it should look something like this format:

bin/
    hello  # an executable file (e.g. $hello) runs the file
pkg/
    darwin_amd64/
        github.com/
            williamqliu/
                go-examples  # a package that we made
                    string
                    string.a  # package object
        golang.org  # a package that we got using go get
            x/
                net/
                tools/
                tour/... # goes down further to more package objects
src/
    github.com/
        williamqliu/
            go-examples/
                hello/
                    hello.go  # our source code
                    hello_test.go  # test code (must have _test in name)
    golang.org
        ...

###Packages

Every Go program is made up of packages. Packages start running in package main. A sample progam looks like:

package main

import(
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10))
}

You need the package name as main and the func as main. You can have additional functions (e.g. say add), but you also have to define main.

A name is exported if it begins with a capital letter. E.g. if you write a function Pi in the math package, you can access this as math.Pi.

###Sample Code

Okay let’s see some sample code already.

package main

import (
    "fmt"
    "math/rand"
    "time"
)

var c, python, java bool  // the 'type' (e.g. bool) goes after the variable name
var d, e int = 1, 2  // you can initialize variables

const Pi = 3.14  // you can make a constant with =. constants cannot be used with := syntax

func add(x, y int) int {  // same as: x int, y int
    return x + y
}

func swap(x, y string) (string, string) {
    return y, x
}

func split(sum int) (x, y int) {  // pass in a named variable 'sum'
    x = sum * 4 / 9
    y = sum - x
    return  // a 'naked' return (i.e. no arguments) returns the named values
}

func main() {
    fmt.Printf("Hello, world\n")  // Default format with Printf
    fmt.Println("My favorite number is", rand.Intn(10))  // Print line

    k := 3  // := is used in place of a 'var' declaration, creates with an implicit type

    for i:=0; i < 10; i++ {
        sum += i
    }
    fmt.Println(sum)

    fmt.Println("These two numbers added together are:", add(42, 13))  // using the 'add' function we made

    a, b := swap("First", "Second")  // using the 'swap' function we made
    fmt.Println(a, b)

    fmt.Println(split(17))  // using the 'split' function we made

    var i int
    fmt.Println(i, c, python, java)  // by default, things are 0 or false (depending on type)

    var c, python, java = true, false, "No!"  // you can initialize variables
    fmt.Println(d, e, c, python, java)

    k := 3  // a short variable declaration means let Go figure out the type
    fmt.Println(k)

    w := 42  // same as: var w int = 42
    x := float64(w)  // doing a type conversion from int to float64, same as: var f float64 = float64(i)
    y := uint(x)  // a uint is a 'byte', same as: var u uint = uint(f)
    z := rune(y)  // a rune is an `int32`, mainly for Unicode code point
    fmt.Println(z)  // If you declare a variable, you have to use it or else it's an error!
    fmt.Println("Pi is", Pi)

    fmt.Println("The time is", time.Now())
}

You can then run the program with:

Control Flow

For loops

Similar to other for loops with an init, a condition expression (evaluated before every iteration), and a post statement (evaluated at end of every iteration). Notice there are no parenthesis surrounding the components of these for loops. The init and post statements are optional (this means that while is for in Go)

sum := 0
for i :=0; i < 10; i++ {
    sum += i
}
fmt.Println(sum)

// no need to setup init and post sections
sum :=1
for ; sum < 1000; {
    sum += sum
}
fmt.Println(sum)

// for-loop is the while loop
sum := 1
for sum < 1000 {
    sum += sum
}
fmt.Println(sum)

// if you omit everything, an infinite loop is created
for {
}

if statements

Just like for-loops, you do not need parenthesis () around an if statement, but {} are required.

if x < 0 {
    fmt.Println("Hey")
}

// You can do a short statement to execute before the condition that is
// only in scope until the end of the if statement
func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    } else {
        fmt.Printf("%g >= %g\n", v, lim)
    }
    return lim
}
fmt.Println(pow(3, 2, 10))

switch statemnets

Switch statements are pretty normal, parsing from top to bottom. If no condition is given, then same as switch true, which helps save long if-else statements.

switch os := runtime.GOOS; os {
case "darwin":
    fmt.Println("OS X")
case "linux":
    fmt.Println("Linux")
default:
    fmt.Printf("%s", os)
}


// switch without a condition
t := time.Now()
switch {
case t.Hour() < 12:
    fmt.Println("Good morning!")
case t.Hour() < 17:
    fmt.Println("Good afternoon.")
default:
    fmt.Println("Good evening.")
}

Defer

A defer statement defers the execution of a function until the surrounding function returns. Deferred call’s arguments are evaluated immediately, but the function call is not executed until the surrounding funtion returns.

defer fmt.Println("world")
fmt.Println("hello")

Deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out order.

fmt.Println("counting")
for i := 0; i < 10; i++ {
    defer fmt.Println(i)
}
fmt.Println("done")

Pointers

Go uses pointers, which hold the memory address of a value.

Some code examples:

var p *int  // * means the pointer
i := 42
p = &i  // p points to i
fmt.Println(*p)  // read i through the pointer p
*p = 21  // set i through pointer p, aka 'dereferencing' or 'indirecting'

Structs

A struct is a collection of fields. You can access struct fields using a dot. Make sure to make variables capitalized so that data is exported outside of the struct (if lowercase, will not export)

type Vertex struct {
    X int
    Y int
}

type main(){
    v := Vertex{1, 2}
    fmt.Println(v)

    // access struct fields using a dot
    v.X = 4
    fmt.Println(v.X)

    // access struct fields through a pointer
    p := &v
    p.X = 1e9  // can also write as (*p).X, but usually no need for explicit dereference
    fmt.Println(v)

}

Struct Literals

A struct literal is a newly allocated struct value by listing the values of its fields. You can list just a subset of the fields using the Name: syntax. & is a special prefix that returns a pointer to the struct value.

type Vertex struct {
    X, Y int
}

var (
    v1 = Vertex{1, 2}  // has type Vertex
    v2 = Vertex{X: 1}  // Y:0 is implicit
    v3 = Vertex{}      // X:0 and Y:0
    p = &Vertex{1, 2}  // has type *Vertex, p is a pointer to a struct allocated on the heap
)

Arrays

An array type [n]T is an array of n values of type T.

e.g. var a [10]int means a variable a as an array of ten integers

An array’s length is part of its type, so arrays cannot be resized.

var a[2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)

// initialize with values
primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)

Array Slices

A slice of an array describes the section of an underlying array, it does not store any data. Any changes to the slices will change the underlying array.

// slice
var s[]int = primes[1:4]
fmt.Println(s)

Array slice literals

An array slice literal is like an array literal without the length.

// array literal
[3]bool{true, true, false}

// creates same array as above, then builds a slice that references it
[]bool{true, true, false}

When slicing, you can omit the high or low bounds to get the defaults. Default is zero for the low bound and the length of the slice for the high bound.

var a [10]int
a[0:10]
a[:10]
a[0:]
a[:]

Array slice length and capacity

A slice has both a length and a capacity.

Use len(s) and cap(s) to get length and capacity of a slice.

s :=[]int{2, 3, 5, 7, 11, 13}

// slice the slice to give it zero length
s = s[:0]

// extend a slice's length
s = s[:4]

// drop its first two values
s = s[2:]

var anothers []int
if anothers == nil{
    fmt.Println("nil")
}

Array slices with make

You can use the built-in make function to create dynamically-sized arrays. The make function allocates a zeroed array and returns a slice that refers to that array.

a := make([]int, 5)  // len(a)=5

// to specify a capacity, pass a third argument to make
b := make([]int, 0, 5)  // len(b)=0, cap(b)=5
b = b[:cap(b)]  // len(b)=5, cap(b)=5
b = b[1:]  // len(b)=4, cap(b)=4

Array slice of slices

Slices can contain any type, including other slices.

// Create a tic-tac-toe board
board := [][]string{
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
}

// players take turns
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
board[1][0] = "O"

Append to a slice

You can use a built-in append to append new elements to a slice.

func append(s []T, vs ...T) []T

What this means is to append to slice s of type T with T values to append. In return you get all the elements of the original slice plus the provided values. If the array s is too small to fit, then a bigger array is allocated.

var s []int
s = append(s, 0)  // append works on nil slices
s = append(s, 1)  // slice grows as needed
s = append(s, 2, 3, 4)  // can add more than one element at a time

Range

You can use range to iterative over a slice or map.

Code example:

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
    fmt.Printf("2**%d = %d\n", i, v)
}

for _, value := range pow {
    fmt.Printf("%d", value)
}

Maps

A map maps keys to values. The zero value of a map is nil. A nil map has no keys, nor can keys be added. The make function returns a map of the given type, initialized and ready to use.

type Vertex struct {
    Lat, Long float64
}

var m map[string]Vertex

func main() {
    m = make(map[string]Vertex)
    m["Bell Labs"] = Vertex{
        40.68, -74.39,
    }
    fmt.Println(m["Bell Labs"])
}

Map Literals

Map literals are like struct literals, except the keys are required.

// Map Literals
type Vertex struct {
    Lat, Long float64
}

var m = map[string]Vertex{
    "Bell Labs": Vertex{
        40.68, -74.39,
    },
    "Google": Vertex{
        37.42, -122.98,
    },
}

// If the top-level type is just a type name, you can omit it from the
// elements of the literal. For example, this is the same as above:
var m = map[string]Vertex{
    "Bell Labs": {40.68, -74.39},
    "Google": {37.42, -122.08},
}

Mutating Maps

You can modify maps with the following actions (assuming a map m):

You can also check that an element is present with a two-value assignment

elem, ok = m[key]
// if 'key' is in 'm', then 'ok' is 'true', else 'ok' is 'false'
// if 'key' is not in 'm', then 'elem' is the zero value for the maps'
// element type

// if 'elem' or 'ok' have not yet been declared, use this short hand:
elem, ok := m[key]

Function values

Functions are values too, meaning they can be passed around like other values.

Methods

Go does not have classes. You can define methods on types.

Code example where the Abs method has a receiver v of type Vertex and returns a float64.

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// can also be written as a regular function with no change in functionality
func Abs(v Vertex) float64{
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

You can declare a method on non-struct types. E.g. numeric type MyFloat with an Abs method.

type MyFloat float64

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

func main() {
    f := MyFloat(-math.Sqrt2)
    fmt.Println(f.Abs())
}

Methods with Pointer Receivers

You can declare methods with pointer receivers. The receiver type has the literal syntax *T for some type T Methods with pointer receivers can modify the value to which the receiver points. Pointer receivers are more common than value receivers.

Interfaces

An interface type is defined as a set of method signatures. A value of interface type can hold any value that implements those methods.

type Abser interface {
    Abs() float64
}

type MyFloat float64

type Vertex struct {
    X, Y float64
}

func main() {
    var a Abser
    f := MyFloat(-math.Sqrt2)
    v := Vertex{3, 4}

    a = f   // a MyFloat implements Abser
    a = &v  // a *Vertex implements Abser

}

Go Tooling

Below are good tools to use:

Go Testing

Make a file with a name ending in _test so that the test runner can pick up, e.g. main_test.go

package main

import "testing"

func TestHandler(t *testing.T) {
    t.Errorf("Something went really wrong")
}

There are also benchmarking options.

Go Load Testing

Use go-wrk to run load testing. Install from:

go get github.com/adjust/go-wrk

You’ll see:

You can see what your code is doing by instrumenting your code.

import (
    "net/http"
    _ "net/http/pprof"
)

Then visit localhost:8080/debug/pprof/ and you can list the heap, the coroutines, number of times garbage collection ran, how long were pauses.

pprof

Use go’s tool pprof to measure the performance of your binary

E.g. `go tool pprof --seconds=5 localhost:8080/`

go-torch

Uber’s go tool to measure the performance of your binary and creates an easier to read “Flame Graph”.

go get github.com/uber/go-torch

Mile High Gophers Meetup

Meetup hosted by bitly

Live Coding Notes

Package manager is govendor, will look at /vendor/ first. * govendor init * govendor fetch github.com/pressly/chi@v2.1 * will appear as /vendor in your github (e.g. go/src/github.com/williamqliu/go-app/vendor/ * imports in as github.com/pressly/chi

Check for package to handle errors nicer so less repeated code

Reflex is language agnostic, pass in shell and does hot reloading

Middleware:

Useful middleware includes:

Structs

Structs represents cursor

type GithubRepo struct {
    FullName string `json:"full_name"`
}

HTTP

Do not use straight http.Get("someurl") - default client used to not have timeout by default? Can check by looking at http.Client Kyle’s live coding without seeing his cursor! Neat, built-in json parsing into standard library

func getRepos(w http.ResponseWriter, r *http.Request) {
    user := chi.URLParam(r, "user")
    urlStr := fmt.Sprintf("https//api.github.com/users/%s/repos", user)
    resp, err := http.Get(urlStr)
    if err != nil {
        log.Printf("%+v", err)
        http.Error(w, http.StatusText(500), 500)
        return
    }

    var repos []*GithubRepo
    json.NewDecoder(resp.Body).Decode(&repos)
    b, err := json.Marshall(repos)
    if err != nil {
        log.Printf("%+v", err)
        http.Error(w, http.StatusText(500), 500)
        return
    }
    w.Header().set("Content-Type", "application/json")

}

Another code example

func (h Handler) Routes() chi.Router {
}
...
at ar.Mount("/repos", reposHandler.Routes())

Documentation

godoc Step through: delve

Code organization:

repos with handlers as packages organize by resource as a package (e.g. models) organize by service as a package

Additional things to look up:

Package SQL provides what you need, no need to have an ORM gorm is an ORM sqlx gives you a little bit of sugar and not a full blown ORM a library writes to a file, then you write the implementation of the database; boltdb

Go Meetup

Jason Keene from Pivotal

A tour of gRPC in Go

What is gRPC?

How to use it

sigint and sigterm to handle interrupt signal

How to protect API from unauthorized access?

How do I roll out new versions of my API?

What happens if things go wrong?

How does it compare to others?

Additional Notes from Meetup

Denver Go Meetup Notes 8-24-2017

Resources

Concourse Talk

Demo with Concourse

https://github.com/jasonkeene/concourse-demo

Summary:

Demo Steps

#####Sample Job - pipeline/main.yml

Code

jobs:
- name: hello
  plan:
  - task: hello
    config:
      platform: linux
      image_resource:  # defines docker image
        type: docker-image
        source:
          repository: golang
    run:
      path: /bin/bash
      args:
      - -c
      - echo 'hello gophers'

Insides of Concourse

Lifecycle

task.yml file

Scheduling

Scheduling is hard

Volume Types

Mounting volume types to a container is a pain. Its a reference to the volume.