Introduction to Go [GDS]

2a32a8864a24ef5c1ce26260d5eebdd6?s=47 timblair
August 24, 2016

Introduction to Go [GDS]

The popularity of the Go programming language has been rising rapidly in the last few years. It's being used in thousands of projects, and people are loving its lightweight approach to static typing, and the power that the built-in concurrency primitives provide.

This deck gives an overview of the core elements of Go that you'll need to know to get going. It covers the type system, explaining and giving examples of the use of each type; the control structures available; the concurrency primitives, and the power that goroutines and channels give; and code organisation, workspaces and tooling.

This talk was given internally at GDS on 2016-08-24.

Fonts:

* Headline One: http://www.dafont.com/headline-hplhs.font
* Ostrich Sans Rounded: http://www.fontsquirrel.com/fonts/ostrich-sans
* Hack: http://sourcefoundry.org/hack/

Colours:

* Main text: #F1F0F2 #395BA6 #202020
* Earendel theme for code: https://github.com/vim-scripts/earendel

2a32a8864a24ef5c1ce26260d5eebdd6?s=128

timblair

August 24, 2016
Tweet

Transcript

  1. Go Go Gopher An Introduction to Go

  2. Go?

  3. History Created by Google Robert Griesemer, Rob Pike & Ken

    Thompson Open-sourced November 2009 Go 1.0 released March 2012
  4. Goals move away from verbose type systems of java /

    c++ ease development of multi-threaded applications easy dependency management & fast compilation “Do less, enable more”
  5. The Language Designed for concurrent systems programming, Strongly and statically

    typed, Compiled, Garbage collected, Object oriented (ish), Small, opinionated, and done.
  6. Hello World package main import ( "fmt" ) func main()

    { fmt.Println("Hello, World!") }
  7. Hello World, the HTTP Version package main import ( "fmt"

    "net/http" ) func handle(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello, World.") } func main() { http.HandleFunc("/", handle) http.ListenAndServe(":8080", nil) }
  8. Types & values

  9. Types Arrays slices maps booleans numerics strings pointers structs channels

  10. Types Classes and Objects Types and Values

  11. Variable Declaration var x T // Variable x of type

    T with a zero value var x T = v // Variable x of type T with value v var x = v // Variable x with value v, implicit typing x := v // Short variable declaration (type inferred) x, y := v1, v2 // Double declaration (similar with var) make(T) // make takes a type T, which must be a slice, // map or channel type, optionally followed by // a type-specific list of expressions new(T) // new allocates zeroed storage for a new // value of type T returns its address
  12. Booleans b1 := true // type is bool b2 :=

    false
  13. Numerics uint8,16,32,64, int8,16,32,64, float32,64, complex64,128 int + uint (32 or

    64 bits, depending on platform) byte (alias for uint8) rune (alias for int32)
  14. Numerics n1 := 123 // int n2 := 123.456 //

    float32/64, depending on arch n3 := 1e10 // float32/64, depending on arch n4 := uint8(123) // uint n5 := float32(123) // float32
  15. Strings s1 := `Raw string literal` s2 := "Interpreted string

    literal" "ᅉ" // UTF-8 input text `ᅉ` // UTF-8 input text as a raw literal "\u65e5" // explicit Unicode code points "\U000065e5" // explicit Unicode code points "\xe6\x97\xa5" // explicit UTF-8 bytes
  16. Runes 'x' '\n' '\U00101234' // range on a string yields

    runes for _, r := range "foo" { fmt.Printf("%s, %T\n", string(r), r) } // f, int32 // o, int32 // o, int32
  17. Pointers var p *int // Variable p of type *int

    (int pointer) i := 42 // Variable i of type int with value 42 p = &i // Assign the address of i to p fmt.Println(p) // Output the value of p (the address of i) fmt.Println(*p) // Output the value of i by using * to // dereference the pointer p
  18. Zero Values 0 // numeric false // boolean "" //

    string nil // pointer, channel, func, // interface, map, or slice
  19. Blank Identifier // Discarding a return value when we don't

    need it if _, err := os.Stat(path); os.IsNotExist(err) { fmt.Printf("%s does not exist\n", path) } // Importing a library just for side effects import _ "mylib"
  20. Type Conversion // T(v) converts the value v to type

    T i := 42 f := float64(i) u := uint(f)
  21. Constants // Untyped constants const i1 = 12345 const f1

    = 1.2345 a1 := i1 * f1 // float64: 15239.9025 // Typed constants const i2 int = 12345 const f2 float64 = 1.2345 a2 := i2 * f2 // compile error
  22. Arrays fixed-length data types contiguous block of elements of the

    same type underlying data structure for both slices and maps The length of an array is part of its type
  23. Arrays var a1 [10]int // Declare int array with a

    length of 10 a1[3] = 42 // Write a value to a specific index i := a[3] // Read a value from a specific index a2 := [3]int{10, 20, 30} // Declare using an array literal a3 := [...]int{10, 20, 30} // Declare with a calculated size a4 := [3]int{1: 10, 2: 20} // Only intialise specific values a5 := [3]int{} // All positions set to zero-value l := len(a5) // Built-in len() function reads array size
  24. Slices Abstracts an underlying array lightweight reference to array +

    length + capacity
  25. Slices s1 = make([]int, 5) // Zeroed int slice: len(s1)

    == 5 s2 = make([]int, 0, 5) // len(s2) == 0, cap(s2) == 5 var s3 []int // Declare a nil slice s4 := []int{1, 2, 3, 4} // Declare and initialise a slice s5 := []string{0:"a", 2:"c"} // ["a", "", "c"] s4 := s2[lo:hi] // Creates a new slice from index lo to hi-1 s5 := s2[1:4] // Slice from index 1 to 3 s6 := s2[:3] // Missing low index implies 0 s7 := s2[3:] // Missing high index implies len(a)
  26. Appending to a Slice func p(x []int) { fmt.Printf("len=%d cap=%d

    %v\n", len(x), cap(x), x) } var s []int // Define a nil int slice p(s) // len=0 cap=0 [] s = append(s, 0) // append() works on nil slices p(s) // len=1 cap=1 [0] s = append(s, 1) // The slice grows as necessary p(s) // len=2 cap=2 [0 1] s = append(s, 2, 3, 4) // append() is variadic p(s) // len=5 cap=6 [0 1 2 3 4]
  27. Maps m1 := make(map[string]int) m1["foo"] = 42 fmt.Println(m1) // map[foo:42]

    m2 := map[string]int{ "a": 1, "b": 2, "c": 3, // trailing comma is required before a newline } fmt.Println(m2) // map[a:1 b:2 c:3]
  28. Mutating Maps m := map[string]int{"a": 1, "b": 2} fmt.Println(m) //

    map[a:1 b:2] m["c"] = 3 fmt.Println(m) // map[a:1 b:2 c:3] delete(m, "b") fmt.Println(m) // map[a:1 c:3] if v, ok := m["b"]; !ok { fmt.Println(v) // 0 (zero value for an int) }
  29. Structs type Rectangle struct { Width int Height int }

    r1 := Rectangle{1, 2} // Create a new Rectangle with w + h r1.Width = 3 // Set the width to a new value fmt.Printf("Width = %d; Height = %d\n", r1.Width, r1.Height)
  30. Structs type Rectangle struct { Width int Height int }

    var r2 Rectangle // Value is nil r3 := Rectangle{Height: 1} // w=0 (int zero value), h=1 r4 := Rectangle{} // w=0, h=0 r5 := &Rectangle{1, 2} // Has type *Rectangle r5.Height = 4 // Dereferencing of struct fields through // a pointer is transparent fmt.Println(r2, r3, r4, r5) // {0 0} {0 1} {0 0} &{1 4}
  31. Struct Embedding type Vehicle struct { speed int passengers int

    } type Weapon struct { damage int } type Tank struct { Vehicle Weapon }
  32. Struct Embedding type Job struct { Command string *log.Logger }

    func NewJob(command string, logger *log.Logger) *Job { return &Job{command, logger} } job := NewJob(...) job.Log("starting now...")
  33. Marshalling Structs import ( "encoding/json" ) type Rectangle struct {

    Width int `json:"width"` Height int `json:"height"` } r := Rectangle{3, 4} j, _ := json.Marshal(r) fmt.Println(string(j)) // {"width":1,"height":2}
  34. Functions func f1() {} // Simple function definition func f2(s

    string, i int) {} // Function that accepts two args func f3(s1, s2 string) {} // Two args of the same type func f4(s ...string) {} // Variadic function func f5() int { // Return type declaration return 42 } func f6() (int, string) { // Multiple return values return 42, "foo" }
  35. Functions // Named return values act like variables func f7()

    (i int, s string) { i = 42 s = "foo" return // Naked return returns current values of vars } f7 := func() {} // Function as a value i := 42 func() { fmt.Println(i) // Function as a closure }()
  36. Functions All function calls are pass-by-value

  37. Functions: defer func ProcessFile(path string) { f, _ := os.Open(path)

    defer f.Close() // Do something with the file... }
  38. Functions: defer func main() { defer func() { fmt.Println("One more

    thing...") }() panic("PANIC!") } // $ go run scratch.go // One more thing... // panic: PANIC!
  39. Methods type Rectangle struct { Width int Height int }

    func (r Rectangle) Area() int { return r.Width * r.Height } func main() { r := Rectangle{Width: 3, Height: 4} fmt.Println(r.Area()) // Area 12 }
  40. Pointer Receivers Use a pointer receiver if you need to...

    1. avoid copying a large data structure 2. mutate the value of the receiver
  41. Pointer Receivers func (r Rectangle) ScaleVal(i int) { r.Width =

    r.Width * i r.Height = r.Height * i } func (r *Rectangle) ScalePtr(i int) { // ... } r := Rectangle{Width: 3, Height: 4} r.ScaleVal(2) fmt.Printf("%+v\n", r) // {Width:3, Height:4} r.ScalePtr(2) fmt.Printf("%+v\n", r) // {Width:6, Height:8}
  42. Interfaces Provide polymorphism no implements keyword interfaces are satisfied implicitly

  43. Interfaces type Speaker interface { Speak() string } type Dog

    struct { } func (d Dog) Speak() string { return "Woof" } type Robot struct { } func (r Robot) Speak() string { return "Danger!" } things := []Speaker{Dog{}, Robot{}} for _, t := range things { fmt.Println(t.Speak()) }
  44. Interface Embedding type Reader interface { Read(p []byte) (n int,

    err error) } type Writer interface { Write(p []byte) (n int, err error) } // ReadWriter is the interface that combines the // Reader and Writer interfaces. type ReadWriter interface { Reader Writer }
  45. All Types are Explicit int != *int != []int !=

    *[]int != []*int
  46. Control Structures

  47. If if x > 0 { // ... } else

    if x < 0 { // ... } else { // ... } if err := doWork(); err != nil { // ... }
  48. For for x := 0; x < 10; x++ {

    // ... } x := false for !x { // ... } for { // ... }
  49. For // If looping over an array, slice, string or

    map, // or reading from a channel, use a range clause // to manage the loop: for index, value := range mySlice { // ... }
  50. For for p, c := range “෭๜ 承" { fmt.Printf("%#U

    starts at byte %d\n", c, p) } // U+65E5 '෭' starts at byte 0 // U+672C '๜' starts at byte 3 // U+0020 ' ' starts at byte 6 // U+8A9E '承' starts at byte 7
  51. Switch x := "b" switch(x) { case "a": // ...

    case "b": // ... default: // ... } switch { case x == "a": // ... fallthrough case x == "b": // ... }
  52. Switch var t interface{} t = functionReturningSomeType() switch t :=

    t.(type) { case string: // String type value case int: // Integer type value case *bool: // Boolean pointer type value default: // Unhandled type }
  53. Error Handling type error interface { Error() string }

  54. Error Handling func Sqrt(f float64) (float64, error) { if f

    < 0 { return 0, errors.New("Square root of negative number") } // ... } f, err := Sqrt(-1) if err != nil { fmt.Println(err) }
  55. Concurrency

  56. Concurrency Go has concurrency primitives built in goroutines for concurrent

    functions channels for communicating between goroutines “Share By Communicating”
  57. Goroutines Lightweight threads of execution multiplexed across os threads very

    cheap to use
  58. goroutines func doWork(i int) { time.Sleep(time.Millisecond * 500) fmt.Println(i) }

    func main() { for i := 1; i <= 5; i++ { doWork(i) } }
  59. goroutines $ time go run g1.go 1 2 3 4

    5 go run g1.go ... 2.757 total
  60. goroutines go someFunc() // Concurrency!

  61. goroutines func doWork(i int) { time.Sleep(time.Millisecond * 500) fmt.Println(i) }

    func main() { for i := 1; i <= 5; i++ { go doWork(i) // concurrency! } }
  62. goroutines $ time go run g2.go go run g2.go ...

    0.247 total
  63. Goroutines import "sync" func main() { var wg sync.WaitGroup wg.Add(1)

    go func() { // Do work... wg.Done() }() wg.Wait() }
  64. GOroutines func main() { var wg sync.WaitGroup for i :=

    1; i <= 5; i++ { wg.Add(1) go func(x int) { defer wg.Done() doWork(x) }(i) } wg.Wait() }
  65. goroutines $ time go run g3.go 4 1 5 2

    3 go run g3.go ... 0.752 total
  66. Channels "Do not communicate by sharing memory; instead, share memory

    by communicating." -- https://golang.org/doc/effective_go.html
  67. Channels ch := make(chan int) ch <- 42 // Send

    a value to the channel v := <-ch // Receive a value from ch
  68. Channels Buffered vs. Unbuffered

  69. Channels c1 := make(chan int) // Unbuffered c2 := make(chan

    int, 0) // Unbuffered c3 := make(chan int, 100) // Buffered
  70. Unbuffered Channels Image from github.com/ardenlabs/gotraining

  71. Buffered Channels Image from github.com/ardenlabs/gotraining

  72. Channels c2 := make(chan int, 1) // A buffered channel

    c2 <- 42 v := <-c2
  73. Channels func doWork(done chan bool) { fmt.Println("Working...") time.Sleep(time.Second) fmt.Println("Done.") done

    <- true } func main() { done := make(chan bool) go doWork(done) <-done }
  74. Channels func doWork(i int, c chan int) { time.Sleep(time.Millisecond *

    500) c <- i } func main() { c := make(chan int) for i := 1; i <= 5; i++ { go doWork(i, c) } for i := 1; i <= 5; i++ { fmt.Println(<-c) } }
  75. Channels ch := make(chan int) close(ch) // Close the channel

    for writing v, ok := <-ch // Receive, testing for closure if !ok { fmt.Println("Channel closed!") }
  76. Channels ch := make(chan int) // Read from channel until

    it is closed for i := range ch { fmt.Println(i) }
  77. Channels A send to a nil channel blocks forever A

    receive on a nil channel blocks forever A send to a closed channel panics A receive on a closed channel returns the zero value
  78. select func doWork(c chan int, done chan struct{}) { select

    { case x := <-c: fmt.Printf("%d received", x) case <-time.After(time.Second * 1): fmt.Println("Timeout") case <-done return } }
  79. Mutexs var counter = struct{ sync.RWMutex m map[string]int }{m: make(map[string]int)}

    counter.RLock() // Read lock n := counter.m["some_key"] counter.RUnlock() fmt.Println("some_key:", n) counter.Lock() // Write lock counter.m["some_key"]++ counter.Unlock()
  80. sync/atomic var ops uint64 for i := 0; i <

    50; i++ { go func() { for { atomic.AddUint64(&ops, 1) runtime.Gosched() } }() } time.Sleep(time.Second) opsFinal := atomic.LoadUint64(&ops) fmt.Println("ops:", opsFinal)
  81. Code Organisation

  82. Packaging all Go programs are organised into packages all .go

    files must declare their package packages must be contained in a single directory names should be short, concise and lower-case
  83. Package Main Package main denotes that a binary should be

    built all executables must have a package main function main() must also exist for binary to be built
  84. Imports import ( "fmt" // Package from stdlib "net/http" //

    Package from stdlib myfmt "mylib/fmt" // Specifying a custom identifier // Import remote stringutil package "github.com/golang/example/stringutil" // Remote package with blank identifier _ "github.com/go-sql-driver/mysql" )
  85. Exporting Identifiers No concept of public / package / private

    identifiers are either exported from a package, or not identifiers starting with a capital are exported
  86. Exporting Identifiers package mylib type Foo string // Exported, accessible

    outside the package type bar string // Only accessible within the mylib package func NewBar(s string) bar { return bar(s) }
  87. Exporting Identifiers package main import "mylib" func main() { //

    This is fine, because Foo is exported from mylib s1 := mylib.Foo("a") // Compile error: "cannot refer to unexported name" s2 := mylib.bar("b") // We can still get a bar value through this function s3 := mylib.NewBar("c") }
  88. Workspaces a workspace is a directory hierarchy must contain src,

    pkg & bin directories in the root $GoPATH gives the location of your workspace Keep all go code in one workspace
  89. Workspaces bin/ hello # command executable pkg/ linux_amd64/ github.com/golang/example/ stringutil.a

    # package object src/ github.com/golang/example/ hello/ hello.go # command source stringutil/ reverse.go # package source reverse_test.go # test source
  90. Tooling

  91. Tooling go build: compile packages & dependencies go run: compile

    & run an application go test: run your test cases go get: download and install packages Godoc: Display & Generate package documentation
  92. Tooling gofmt: format your code in the only acceptable way

    goimports: go fmt + import management golint: checks code for style violations go vet: find common coding errors oracle: source analysis tool
  93. and finally...

  94. Find out more https://golang.org/ref/spec https://tour.golang.org/ https://golang.org/doc/code.html https://golang.org/doc/effective_go.html

  95. The end @timblair