Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to Go

timblair
September 24, 2015

Introduction to Go

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 as part of the regular lunchtime "Tech Talks" series at Venntro. A video of the talk is available at https://vimeo.com/140410716 (66 minutes).

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

timblair

September 24, 2015
Tweet

More Decks by timblair

Other Decks in Programming

Transcript

  1. Go?

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

    Thompson Open-sourced November 2009 Go 1.0 released March 2012
  3. 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”
  4. The Language Designed for concurrent systems programming, Strongly and statically

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

    func main() { fmt.Println("Hello, World!") }
  6. 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) }
  7. 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
  8. Zero Values 0 // numeric false // boolean "" //

    string nil // pointer, channel, func, // interface, map, or slice
  9. Blank Identifier // Importing a library just for side effects

    import _ "mylib" ! // 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) }
  10. Type Conversion // T(v) converts the value v to type

    T i := 42 f := float64(i) u := uint(f)
  11. 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)
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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)
  18. 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]
  19. 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]
  20. 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) }
  21. 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
  22. 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)
  23. 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}
  24. Struct Embedding type Vehicle struct { speed int passengers int

    } ! type Weapon struct { damage int } ! type Tank struct { Vehicle Weapon }
  25. 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...")
  26. 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}
  27. 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" }
  28. 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 }()
  29. Functions: defer func ProcessFile(path string) { f, _ := os.Open(path)

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

    thing...") }() ! panic("PANIC!") } ! // $ go run scratch.go // One more thing... // panic: PANIC!
  31. 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 }
  32. Pointer Receivers Use a pointer receiver if you need to...

    1. avoid copying a large data structure 2. mutate the value of the receiver
  33. 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}
  34. 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()) }
  35. 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 }
  36. If if x > 0 { // ... } else

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

    // ... } ! x := false for !x { // ... } ! for { // ... }
  38. 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 { // ... }
  39. 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
  40. Switch x := "b" switch(x) { case "a": // ...

    case "b": // ... default: // ... } switch { case x == "a": // ... fallthrough case x == "b": // ... }
  41. 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 }
  42. 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) }
  43. Concurrency Go has concurrency primitives built in goroutines for concurrent

    functions channels for communicating between goroutines “Share By Communicating”
  44. goroutines func doWork(i int) { time.Sleep(time.Millisecond * 500) fmt.Println(i) }

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

    5 go run g1.go ... 2.757 total
  46. 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! } }
  47. GOroutines func doWork(i int) { time.Sleep(time.Millisecond * 500) fmt.Println(i) }

    ! 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() }
  48. goroutines $ time go run g3.go 4 1 5 2

    3 go run g3.go ... 0.752 total
  49. Channels c1 := make(chan int) // Create an unbuffered channel

    of type int c1 <- 42 // Send a value to the channel v := <-c1 // Receive a value from ch ! // The above will deadlock, because a write to an unbuffered // channel will block until the value has been received ! c2 := make(chan int, 1) // Create a buffered channel c2 <- 42 v := <-c2
  50. Channels c3, c4 := make(chan int), make(chan int) ! close(c3)

    // Close the channel for writing v, ok := <-c3 // Receive, testing for closure if !ok { fmt.Println("Channel closed!") } ! // Read from channel until it is closed for i := range c4 { fmt.Println(i) } ! c5 := make(chan chan int) // A channel of int channels
  51. Channels var done chan bool ! func doWork() { fmt.Println("Working...")

    time.Sleep(time.Second) fmt.Println("Done.") done <- true } ! func main() { go doWork() <-done }
  52. 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++ { i := <-c fmt.Println(i) } }
  53. 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
  54. 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 } }
  55. Concurrency example: Worker Pools func doWork(queue <-chan int, results chan<-

    int) { for i := range queue { time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000))) results <- i * 2 } } ! func publishJobs(jobs int, queue chan<- int) { for i := 1; i <= jobs; i++ { queue <- i } close(queue) }
  56. func startWorkers(c int, queue <-chan int, results chan<- int) {

    var wg sync.WaitGroup ! for i := 1; i <= c; i++ { wg.Add(1) go func() { defer wg.Done() doWork(queue, results) }() } ! go func() { wg.Wait() close(results) }() } Concurrency example: Worker Pools
  57. func main() { queue := make(chan int) results := make(chan

    int) ! go startWorkers(5, queue, results) go publishJobs(queue) ! for r := range results { fmt.Println(r) } } Concurrency example: Worker Pools
  58. 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()
  59. sync/atomic var ops uint64 = 0 ! 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)
  60. 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
  61. 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
  62. 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" )
  63. Exporting Identifiers No concept of public / package / private

    identifiers are either exported from a package, or not identifiers starting with a capital are exported
  64. 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) }
  65. 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") }
  66. 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
  67. 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
  68. Tooling go build: compile packages & dependencies go run: compile

    & run an application go test: run your test cases go get: download and install packages
  69. 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
  70. What for? Game backends, crawlers & scrapers, web services, queueing,

    HTTP caching, logging, data stores, service coordination, real-time communication, WebSockets, HTTP routing, metric, analytics, infrastructure management, provisioning...