SlideShare a Scribd company logo
Go Containers
January 23, 2014 

John Graham-Cumming

www.cloudflare.com!
Six interesting containers
•  From pkg/container!
•  container/list!
•  container/ring!
•  container/heap

!
•  Built in
•  map!
•  slice
•  Channels as queues

www.cloudflare.com!
container/list!
•  Doubly-linked list implementation
•  Uses interface{} for values
l := list.New()!
e0 := l.PushBack(42)!
e1 := l.PushFront(13)!
Pity	
  there’s	
  no	
  ‘map’	
  
e2 := l.PushBack(7)!
func4on	
  
l.InsertBefore(3, e0)!
l.InsertAfter(196, e1)!
l.InsertAfter(1729, e2)!
!
for e := l.Front(); e != nil; e = e.Next() {!
fmt.Printf("%d ", e.Value.(int))!
}!
e.Value	
  to	
  get	
  the	
  
fmt.Printf("n")!
stored	
  value	
  
!
!
13 196 3 42 7 1729!
www.cloudflare.com!
container/list!

All	
  work	
  on	
  
elements	
  not	
  values	
  	
  

l.MoveToFront(e2)!
l.MoveToBack(e1)!
Returns	
  the	
  value	
  
l.Remove(e0)!
removed	
  
!
for e := l.Front(); e != nil; e = e.Next() {!
fmt.Printf("%d ", e.Value.(int))!
}!
fmt.Printf("n")!
!
!
7 196 3 1729 13!

www.cloudflare.com!
container/ring!
•  A circular ‘list’
parus := []string{”major", “holsti”, “carpi”}!
!
r := ring.New(len(parus))!
for i := 0; i < r.Len(); i++ {!
r.Value = parus[i]!
r = r.Next()!
}!
!
r.Do(func(x interface{}) {!
fmt.Printf(“Parus %sn”, x.(string))!
})!

•  Move n elements through ring
r.Move(n)!

www.cloudflare.com!
container/heap!
•  Implements a “min-heap” (i.e. tree where each node is

the “minimum” element in its subtree)

•  Needs a notion of “Less” and a way to “Swap”
www.cloudflare.com!
container/heap!
•  The single most confusing definition in all of Go
type Interface interface {!
sort.Interface!
Push(x interface{}) // add x as element Len()!
Pop() interface{}
// remove/return element Len()-1!
}!
!
// Note that Push and Pop in this interface are for !
// package heap's implementation to call. To add and !
// remove things from the heap, use heap.Push and!
// heap.Pop.!

www.cloudflare.com!
container/heap!
•  Simple example
type OrderedInts []int!
!
func (h OrderedInts) Len() int { return len(h) }!
func (h OrderedInts) Less(i, j int) bool {!
return h[i] < h[j]!
}!
func (h OrderedInts) Swap(i,j int) {h[i],h[j]=h[j],h[i]}!
func (h *OrderedInts) Push(x interface{}) {!
!*h = append(*h, x.(int))!
}!
func (h *OrderedInts) Pop() interface{} {!
!old := *h!
!n := len(old)-1!
!x := old[n]!
!*h = old[:n]!
!return x!
}!
www.cloudflare.com!
container/heap!
•  Using a heap
h := &OrderedInts{33,76,55,24,48,63,86,83,83,12}!
!
heap.Init(h)!
!
fmt.Printf("min: %dn", (*h)[0])!
!
for h.Len() > 0 {!
fmt.Printf("%d ", heap.Pop(h))!
}!
!
fmt.Printf(“n”)!

www.cloudflare.com!
container/heap!
•  Heaps are useful for...
•  Make a priority queue
•  Sorting
•  Graph algorithms

www.cloudflare.com!
MAP

www.cloudflare.com!
map!
•  Maps are typed



dictionary := make(map[string]string)!
dictionary := map[string]string{}!
!

•  They are not concurrency safe
•  Use a lock or channel for concurrent read/write access
counts := struct{!
sync.RWMutex!
m map[string]int!
}{m: make(map[string]int)}!
!
counts.RLock()!
fmt.Printf(“foo count”, counts.m[“foo”]!
counts.RUnlock()!
!
counts.Lock()!
counts.m[“foo”] += num_foos!
counts.Unlock()!
!
www.cloudflare.com!

Mul4ple	
  readers,	
  
one	
  writer	
  
map iteration
m := map[string]int{!
"bar”: 54,!
"foo”: 42,!
"baz”: -1,!
}!
!
for k := range m {!
// k is foo, bar, baz!
}!
!
for _, v := range m {!
// v is 54, 42, -1 in some order!
}!
!
for k, v := range m {!
// k and v are as above!
}!

www.cloudflare.com!

Order	
  of	
  itera4on	
  is	
  
undefined	
  	
  
Common map operations
•  Remove an element
delete(dictionary, “twerking”)!

•  Test presence of an element
definition, present := dictionary[“hoopy”]!
!
_, present := dictionary[“sigil”]!

•  Missing element gives a “zero” value




fmt.Printf(“[%s]n”, dictionary[“ewyfgwyegfweygf”])!
!
[]!

www.cloudflare.com!
SLICE

www.cloudflare.com!
Slices
•  A slice is part of an array
var arrayOfInts [256]int!
!
var part []int = arrayOfInts[2:6]!

•  arrayOfInts is 256 ints contiguously in memory
0	
  

1	
  

2	
  

3	
  

4	
  

5	
  

6	
  

7	
  

8	
  

9	
  

10	
  

11	
  

12	
  

!
!
•  part consists of a pointer (to arrayOfInts[2]) and a
length (4)

www.cloudflare.com!
Slice passing
•  A slice is passed (like everything else) by copy
var arrayOfInts [256]int!
!
var part []int = arrayOfInts[2:6]!
!
func fill(s []int) {!
for i, _ := range s {!
s[i] = i*2!
}!
!
s = s[1:]!
}!
Does	
  nothing	
  to	
  
!
part!
fill(part)!
fmt.Printf(“%#v”, part)!
!
% ./slice!
[]int{0, 2, 4, 6}!
www.cloudflare.com!

Contents	
  of	
  s	
  can	
  be	
  
modified	
  

Changes	
  contents	
  of	
  
underlying	
  array	
  
Slice passing, part 2
•  Can pass a pointer to a slice to modify the slice
var arrayOfInts [256]int!
!
var part intSlice = arrayOfInts[2:6] !
!
Contents	
  of	
  s	
  can	
  be	
  
type intSlice []int!
modified	
  and	
  s	
  can	
  
func (s *intSlice) fill() {!
be	
  changed	
  
for i, _ := range *s {!
(*s)[i] = i*2!
}!
*s = (*s)[1:]!
Changes	
  part!
}!
!
part.fill()!
fmt.Printf("%#vn", part)!
!
% ./slice!
[]int{2, 4, 6}!
www.cloudflare.com!
Slice iteration
prime := []int{2, 3, 5, 7, 11}!
!
for i := range prime {!
// i is 0, 1, 2, 3, 4!
}!
!
for _, e := range prime{!
// e is 2, 3, 5, 7, 11!
}!
!
for i, e := range prime {!
// i and e as above!
}!

www.cloudflare.com!
Copying slices
•  copy builtin
morePrimes := make([]int, len(primes), 2*cap(primes))!
!
copy(morePrimes, primes)!

•  copy allows source and destination to overlap
primes := [10]int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}!
odds := primes[1:7]!
!
odds = odds[0:len(odds)+1]!
copy(odds[4:], odds[3:])!
odds[3] = 9!
fmt.Printf("%#vn", odds)!
!
[]int{3, 5, 7, 9, 11, 13, 17}!

www.cloudflare.com!
Appending slices
s := []int{1, 3, 6, 10}!
t := []int{36, 45, 55, 66, 78}!
!
s = append(s, 15)!
Adding	
  individual	
  
elements	
  
s = append(s, 21, 28)!
!
s = append(s, t...)!
!
Adding	
  an	
  en4re	
  
nu := append([]int(nil), s...)!
slice	
  
!
s = append(s, s...)!
Copying	
  a	
  slice	
  (use	
  
!
copy	
  instead)	
  
fmt.Printf(“%#vn”, s)!
!
[]int{1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 1, 3,
6, 10, 15, 21, 28, 36, 45, 55, 66, 78}!

www.cloudflare.com!
CHANNELS AS QUEUES

www.cloudflare.com!
A buffered channel is a FIFO queue 
•  A typed queue of up to 10 Things



queue := make(chan Thing, 10)

!

•  Get the next element from the queue if there is one



select {!
case t := <-queue: // got one!
default:
// queue is empty!
}
!

•  Add to queue if there’s room
select {!
case queue <- t: // added to queue!
default:
// queue is full!
}!

www.cloudflare.com!
GENERICS

www.cloudflare.com!
Perhaps heretical
•  But... I wish Go had some generics
•  interface{} is like void *; Type assertions similar to casts
l := list.New()!
l.PushFront("Hello, World!")!
v := l.Front()!
i := v.Value.(int)!
% go build l.go!
% ./l!
panic: interface conversion: interface is
string, not int!
!
goroutine 1 [running]:!
runtime.panic(0x49bdc0, 0xc210041000)!
!/extra/go/src/pkg/runtime/panic.c:266
+0xb6!
main.main()!
!/extra/src/mc/generic.go:12 +0xaa!
www.cloudflare.com!
Sources etc.
•  Slides and code samples for this talk:

https://github.com/cloudflare/jgc-talks/tree/master/
Go_London_User_Group/Go_Containers

•  All my talks (with data/code) on the CloudFlare Github

https://github.com/cloudflare/jgc-talks
•  All my talks on the CloudFlare SlideShare

http://www.slideshare.net/cloudflare

www.cloudflare.com!

More Related Content

Go Containers

  • 1. Go Containers January 23, 2014 John Graham-Cumming www.cloudflare.com!
  • 2. Six interesting containers •  From pkg/container! •  container/list! •  container/ring! •  container/heap
 ! •  Built in •  map! •  slice •  Channels as queues www.cloudflare.com!
  • 3. container/list! •  Doubly-linked list implementation •  Uses interface{} for values l := list.New()! e0 := l.PushBack(42)! e1 := l.PushFront(13)! Pity  there’s  no  ‘map’   e2 := l.PushBack(7)! func4on   l.InsertBefore(3, e0)! l.InsertAfter(196, e1)! l.InsertAfter(1729, e2)! ! for e := l.Front(); e != nil; e = e.Next() {! fmt.Printf("%d ", e.Value.(int))! }! e.Value  to  get  the   fmt.Printf("n")! stored  value   ! ! 13 196 3 42 7 1729! www.cloudflare.com!
  • 4. container/list! All  work  on   elements  not  values     l.MoveToFront(e2)! l.MoveToBack(e1)! Returns  the  value   l.Remove(e0)! removed   ! for e := l.Front(); e != nil; e = e.Next() {! fmt.Printf("%d ", e.Value.(int))! }! fmt.Printf("n")! ! ! 7 196 3 1729 13! www.cloudflare.com!
  • 5. container/ring! •  A circular ‘list’ parus := []string{”major", “holsti”, “carpi”}! ! r := ring.New(len(parus))! for i := 0; i < r.Len(); i++ {! r.Value = parus[i]! r = r.Next()! }! ! r.Do(func(x interface{}) {! fmt.Printf(“Parus %sn”, x.(string))! })! •  Move n elements through ring r.Move(n)! www.cloudflare.com!
  • 6. container/heap! •  Implements a “min-heap” (i.e. tree where each node is the “minimum” element in its subtree) •  Needs a notion of “Less” and a way to “Swap” www.cloudflare.com!
  • 7. container/heap! •  The single most confusing definition in all of Go type Interface interface {! sort.Interface! Push(x interface{}) // add x as element Len()! Pop() interface{} // remove/return element Len()-1! }! ! // Note that Push and Pop in this interface are for ! // package heap's implementation to call. To add and ! // remove things from the heap, use heap.Push and! // heap.Pop.! www.cloudflare.com!
  • 8. container/heap! •  Simple example type OrderedInts []int! ! func (h OrderedInts) Len() int { return len(h) }! func (h OrderedInts) Less(i, j int) bool {! return h[i] < h[j]! }! func (h OrderedInts) Swap(i,j int) {h[i],h[j]=h[j],h[i]}! func (h *OrderedInts) Push(x interface{}) {! !*h = append(*h, x.(int))! }! func (h *OrderedInts) Pop() interface{} {! !old := *h! !n := len(old)-1! !x := old[n]! !*h = old[:n]! !return x! }! www.cloudflare.com!
  • 9. container/heap! •  Using a heap h := &OrderedInts{33,76,55,24,48,63,86,83,83,12}! ! heap.Init(h)! ! fmt.Printf("min: %dn", (*h)[0])! ! for h.Len() > 0 {! fmt.Printf("%d ", heap.Pop(h))! }! ! fmt.Printf(“n”)! www.cloudflare.com!
  • 10. container/heap! •  Heaps are useful for... •  Make a priority queue •  Sorting •  Graph algorithms www.cloudflare.com!
  • 12. map! •  Maps are typed dictionary := make(map[string]string)! dictionary := map[string]string{}! ! •  They are not concurrency safe •  Use a lock or channel for concurrent read/write access counts := struct{! sync.RWMutex! m map[string]int! }{m: make(map[string]int)}! ! counts.RLock()! fmt.Printf(“foo count”, counts.m[“foo”]! counts.RUnlock()! ! counts.Lock()! counts.m[“foo”] += num_foos! counts.Unlock()! ! www.cloudflare.com! Mul4ple  readers,   one  writer  
  • 13. map iteration m := map[string]int{! "bar”: 54,! "foo”: 42,! "baz”: -1,! }! ! for k := range m {! // k is foo, bar, baz! }! ! for _, v := range m {! // v is 54, 42, -1 in some order! }! ! for k, v := range m {! // k and v are as above! }! www.cloudflare.com! Order  of  itera4on  is   undefined    
  • 14. Common map operations •  Remove an element delete(dictionary, “twerking”)! •  Test presence of an element definition, present := dictionary[“hoopy”]! ! _, present := dictionary[“sigil”]! •  Missing element gives a “zero” value fmt.Printf(“[%s]n”, dictionary[“ewyfgwyegfweygf”])! ! []! www.cloudflare.com!
  • 16. Slices •  A slice is part of an array var arrayOfInts [256]int! ! var part []int = arrayOfInts[2:6]! •  arrayOfInts is 256 ints contiguously in memory 0   1   2   3   4   5   6   7   8   9   10   11   12   ! ! •  part consists of a pointer (to arrayOfInts[2]) and a length (4) www.cloudflare.com!
  • 17. Slice passing •  A slice is passed (like everything else) by copy var arrayOfInts [256]int! ! var part []int = arrayOfInts[2:6]! ! func fill(s []int) {! for i, _ := range s {! s[i] = i*2! }! ! s = s[1:]! }! Does  nothing  to   ! part! fill(part)! fmt.Printf(“%#v”, part)! ! % ./slice! []int{0, 2, 4, 6}! www.cloudflare.com! Contents  of  s  can  be   modified   Changes  contents  of   underlying  array  
  • 18. Slice passing, part 2 •  Can pass a pointer to a slice to modify the slice var arrayOfInts [256]int! ! var part intSlice = arrayOfInts[2:6] ! ! Contents  of  s  can  be   type intSlice []int! modified  and  s  can   func (s *intSlice) fill() {! be  changed   for i, _ := range *s {! (*s)[i] = i*2! }! *s = (*s)[1:]! Changes  part! }! ! part.fill()! fmt.Printf("%#vn", part)! ! % ./slice! []int{2, 4, 6}! www.cloudflare.com!
  • 19. Slice iteration prime := []int{2, 3, 5, 7, 11}! ! for i := range prime {! // i is 0, 1, 2, 3, 4! }! ! for _, e := range prime{! // e is 2, 3, 5, 7, 11! }! ! for i, e := range prime {! // i and e as above! }! www.cloudflare.com!
  • 20. Copying slices •  copy builtin morePrimes := make([]int, len(primes), 2*cap(primes))! ! copy(morePrimes, primes)! •  copy allows source and destination to overlap primes := [10]int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}! odds := primes[1:7]! ! odds = odds[0:len(odds)+1]! copy(odds[4:], odds[3:])! odds[3] = 9! fmt.Printf("%#vn", odds)! ! []int{3, 5, 7, 9, 11, 13, 17}! www.cloudflare.com!
  • 21. Appending slices s := []int{1, 3, 6, 10}! t := []int{36, 45, 55, 66, 78}! ! s = append(s, 15)! Adding  individual   elements   s = append(s, 21, 28)! ! s = append(s, t...)! ! Adding  an  en4re   nu := append([]int(nil), s...)! slice   ! s = append(s, s...)! Copying  a  slice  (use   ! copy  instead)   fmt.Printf(“%#vn”, s)! ! []int{1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78}! www.cloudflare.com!
  • 23. A buffered channel is a FIFO queue •  A typed queue of up to 10 Things queue := make(chan Thing, 10) ! •  Get the next element from the queue if there is one select {! case t := <-queue: // got one! default: // queue is empty! } ! •  Add to queue if there’s room select {! case queue <- t: // added to queue! default: // queue is full! }! www.cloudflare.com!
  • 25. Perhaps heretical •  But... I wish Go had some generics •  interface{} is like void *; Type assertions similar to casts l := list.New()! l.PushFront("Hello, World!")! v := l.Front()! i := v.Value.(int)! % go build l.go! % ./l! panic: interface conversion: interface is string, not int! ! goroutine 1 [running]:! runtime.panic(0x49bdc0, 0xc210041000)! !/extra/go/src/pkg/runtime/panic.c:266 +0xb6! main.main()! !/extra/src/mc/generic.go:12 +0xaa! www.cloudflare.com!
  • 26. Sources etc. •  Slides and code samples for this talk: https://github.com/cloudflare/jgc-talks/tree/master/ Go_London_User_Group/Go_Containers •  All my talks (with data/code) on the CloudFlare Github https://github.com/cloudflare/jgc-talks •  All my talks on the CloudFlare SlideShare http://www.slideshare.net/cloudflare www.cloudflare.com!