Just Enough Golang - keshavbaweja-git/guides GitHub Wiki

Declare a slice

var results []string

Append to a slice

results = append(results, result)

Append one slice to another

a := []int{1, 2}
b := []int{11, 22}
a = append(a, b...) // a == [1 2 11 22]

Split a string by space

words := strings.Fields(str)

Split a string by comma

words := strings.Split(str, ",")

Convert string to integer

i, err := strconv.Atoi("34")

Trim whitespace from a string

result := strings.TrimSpace(s)

Runes

Overview

# Convert string to rune slice
r := []rune("ABC€")
fmt.Println(r)        // [65 66 67 8364]
fmt.Printf("%U\n", r) // [U+0041 U+0042 U+0043 U+20AC]

# Convert rune slice to string
s := string([]rune{'\u0041', '\u0042', '\u0043', '\u20AC', -1})
fmt.Println(s) // ABC€�

goroutines

  • Unit of concurrency in go program
  • Not OS Thread
  • Not Green Thread - Thread managed by language runtime
  • Coroutines - concurrent subroutines that are nonpreemptive (i.e. can't be interrupted)
  • Go runtime observers runtime behavior of goroutines and automatically suspends them when they block and then resumes them when they are unblocked
  • goroutines are implicitly concurrent constructs, with parallelism provided by Go runtime
  • Go runtime schedules goroutines onto a M:N scheduler, which provides M Green threads mapped to N OS threads
  • Go follows a fork-join model of concurrency

Simple go routines

func main() {
 go sayHello()
}

func sayHello() {
 fmt.Println("Hello")
}
go func() {
 fmt.Println("Hello")
}()

go routines with join point implemented by Wait Group

var wg sync.WaitGroup
sayHello := func() {
 defer wg.Done()
 fmt.Println("Hello")
}
wg.Add(1)
go sayHello()
wg.Wait()

go routines "Close" around the lexical scope they are declared in, thus capturing variables

### World is printed out
var wg WaitGroup
greeting := "Hello"
sayHello := func() {
 defer wg.Done()
 greeting = "World"
}
wg.Add(1)
go sayHello()
wg.Wait()
fmt.Println(greeting)

Channels

  • Channels are an important synchronisation primitive in golang. Channels are used to implement synchronisation by providing a communication mechanism among goroutines.
# Declare a bidirectional channel
var dataChannel chan interface{}
dataChannel = make(chan, interface{})

# Declare a read channel, that can only be read from
var readChannel <-chan interface{}
readChannel = make(<-chan, interface{})

# Declare a write channel, that can only be written to
var writeChannel chan<- interface{}
writeChannel = make(chan<-, interface{})
  • Go implicitly converts bidirectional channel to unidirectional channel based on method signature
  • Channels are typed
greetingChannel := make(chan, string)
go greet() {
 greetingChannel <- "Hello"
}()
fmt.Println(<-greetingChannel)
  • Reading from a write-only channel, writing to a read-only channel produced compile time errors
  • Channels are blocking. Reading from an empty channel, writing to a full channel are blocking operations
  • Blocking nature of channels can result in a deadlock if operations are not structured correctly
greetingChannel := make(chan, string)
go greet() {
 if true {
  return
 }
 greetingChannel <- "Hello"
}()
fmt.Println(<-greetingChannel)
  • Reading a channel can optionally return a second return value - a boolean status that indicates if the value returned is from channel buffer of default nil value of a channel that has been closed.
greetingChannel := make(chan, string)
go greet() {
 greetingChannel <- "Hello"
}()
greeting, ok := <-greetingChannel
  • range keyword used in conjunction with for loop supports Channels, and will break out when a channel has been closed
counterChannel := make(chan, int)
go count() {
 defer close(counterChannel)
 for i := 1; i <=5; i++ {
  counterChannel <- i
 }
}()
for count : range counterChannel {
 fmt.Println(counter)
}

Barrier implementation with Channels

barrierChannel := make(chan, interface{})
var wg sync.WaitGroup
for i:=0; i<5; i++ {
 wg.Add(1)
 go work() {
  defer(wg.Done())
  <-barrierChannel
  fmt.Println("Starting work") 
 }()
}
close(barrierChannel)
wg.Wait()

Safe channel programming constructs

  • Channel ownership - Channel owner is a goroutine that is responsible for instantiating a channel, writing to it and closing the channel.
  • Channel users have read only view of a channel.
channelOwner := func() <-chan int {
 counterChannel := make(chan int)
 go func() {
  defer close(counterChannel)
  for int i := 0; i < 5; i++ {
   counterChannel <- i
  }
 }()
 return counterChannel
}

counterChannel := channelOwner()
for count := range counterChannel {
 fmt.Printf("Received %d\n", count) 
}
fmt.Println("Done")

Context

Context