| Go for C++ developers |
| |
| Francesc Campoy |
| Developer, Advocate, and Gopher at Google |
| @francesc |
| campoy@golang.org |
| |
| #go4cpp |
| |
| * Go for C++ developers |
| |
| Agenda: |
| |
| - let's talk about Go |
| |
| - how do I do X in Go? |
| |
| - concurrency, because it's awesome |
| |
| - Q & A: but feel free to interrupt anytime |
| |
| - We might not go through all the slides |
| |
| * About me |
| |
| *2011* Joined Google as a Software Engineer |
| |
| - writing mostly in C++ and Python |
| - used to think _smart_code_ was better than boring code |
| |
| *2012* Joined the Go team as a Developer Programs Engineer |
| |
| *2014*to*today* Developer Advocate for the Google Cloud Platform |
| |
| * Let's talk about Go |
| |
| * Go is |
| |
| Go is |
| |
| - open source |
| |
| - statically typed |
| |
| - object oriented (if you ask me) |
| |
| - compiled |
| |
| - memory safe |
| |
| - type safe |
| |
| * Why Go? |
| |
| Go was created for: |
| |
| - scalability |
| |
| - concurrency |
| |
| - simplicity |
| |
| * Who uses Go? |
| |
| Google: |
| |
| - YouTube |
| - dl.google.com |
| |
| Others: |
| |
| - Docker |
| - SoundCloud |
| - Canonical |
| - CloudFlare |
| - Mozilla |
| - ... |
| |
| [[http://golang.org/wiki/GoUsers][golang.org/wiki/GoUsers]] |
| |
| * Who uses Go? |
| |
| .image go4cpp/trends.png _ 800 |
| |
| .caption Google Trends for [[http://www.google.com/trends/explore#q=golang][golang]] |
| |
| * Some Go features |
| |
| * Go types |
| |
| - primitive types |
| |
| int, uint, int8, uint8, ... |
| bool, string |
| float32, float64 |
| complex64, complex128 |
| |
| - structs |
| |
| struct { |
| Name string |
| Age int |
| } |
| |
| - slices and arrays |
| |
| []int, [3]string, []struct{ Name string } |
| |
| - maps |
| |
| map[string]int |
| |
| * Kinds of types (continued) |
| |
| - pointers |
| |
| *int, *Person |
| |
| - functions |
| |
| func(int, int) int |
| |
| - channels |
| |
| chan bool |
| |
| - interfaces |
| |
| interface { |
| Start() |
| Stop() |
| } |
| |
| * Type declarations |
| |
| type [name] [specification] |
| |
| `Person` is a `struct` type. |
| |
| type Person struct { |
| name string |
| age int |
| } |
| |
| `Celsius` is a `float64` type. |
| |
| type Celsius float64 |
| |
| * Function declarations |
| |
| func [name] ([params]) [return value] |
| func [name] ([params]) ([return values]) |
| |
| A sum function: |
| |
| func sum(a int, b int) int { |
| return a + b |
| } |
| |
| A function with multiple return values: |
| |
| func divMod(a, b int) (int, int) { |
| return a / b, a % b |
| } |
| |
| Made clearer by naming the return values: |
| |
| func divMod(den, div int) (quo, rem int) { |
| return den / div, den % div |
| } |
| |
| * Method declarations |
| |
| func ([receiver]) [name] ([params]) ([return values]) |
| |
| A method on a struct: |
| |
| func (p Person) IsMinor() bool { |
| return p.age < 18 |
| } |
| |
| But also a method on a `float64`: |
| |
| func (c Celsius) Freezing() bool { |
| return c <= 0 |
| } |
| |
| _Constraint:_ Methods can be defined *only* on types declared in the same package. |
| |
| // This won't compile |
| func (s string) Length() int { return len(s) } |
| |
| * Declaring variables |
| |
| Normal declaration: |
| |
| var text string = "hello" |
| |
| You can omit types: |
| |
| var text = "hello" |
| |
| And inside of functions: |
| |
| text := "hello" |
| |
| Other types |
| |
| a := 0 // int |
| b := true // boolean |
| f := 1.0 // float64 |
| p := Person{"Francesc", "Campoy"} // Person |
| |
| * No implicit numeric conversion |
| |
| Given types: |
| |
| type Celsius float64 |
| |
| type Fahrenheit float64 |
| |
| And the variables: |
| |
| var freezing Fahrenheit = 32 |
| var boiling Celsius = 100 |
| |
| This code won't compile: |
| |
| sauna := (freezing + boiling) / 2 |
| |
| There's no implicit numeric conversion in Go. |
| |
| * Pointers and memory allocation |
| |
| * Pointers |
| |
| Go has pointers: |
| |
| var p *int |
| p = new(int) |
| |
| But no pointer arithmetics: |
| |
| var p *int = &a[0] |
| var q = p+2 // invalid |
| |
| There's `new` but there's no `delete`. |
| |
| Memory is garbaged collected after it's no longer accessible. |
| |
| * Memory allocation |
| |
| The compiler decides where to allocate based on escape analysis. |
| |
| Using `new` doesn't imply using the heap: |
| |
| `stack.go`: |
| |
| func get() int { |
| n := new(int) |
| return *n |
| } |
| |
| And not all values in the heap are created with `new`: |
| |
| `heap.go`: |
| |
| func get() *int { |
| n := 4 |
| return &n |
| } |
| |
| * Choosing what allocation you want |
| |
| You can *not* decide where a value is allocated. |
| |
| But you can see what kind of allocation is used: |
| |
| $ go tool 6g -m stack.go |
| |
| stack.go:3: can inline get |
| stack.go:4: get new(int) does not escape |
| |
| Compare to: |
| |
| $ go tool 6g -m heap.go |
| |
| heap.go:3: can inline get |
| heap.go:4: moved to heap: n |
| heap.go:5: &n escapes to heap |
| |
| * RAII |
| |
| _Resource_Acquisition_Is_Initialization_ |
| |
| Provides: |
| |
| - encapsulation of acquisition and disposition of resources |
| |
| - exception safe |
| |
| An example: |
| |
| void write_to_file (const std::string & message) { |
| // mutex to protect file access |
| static std::mutex mutex; |
| |
| // lock mutex before accessing file |
| // at the end of the scope unlock mutex |
| std::lock_guard<std::mutex> lock(mutex); |
| |
| // mutual exclusion access section |
| ... |
| } |
| |
| * RAII in Go: defer |
| |
| The `defer` statement: |
| |
| - schedules a function call at the end of the current function |
| |
| - stacks all deferred calls - last in first out |
| |
| var m sync.Mutex |
| |
| func writeToFile(msg string) error { |
| m.Lock() |
| defer m.Unlock() |
| |
| // mutual exclusion access section |
| } |
| |
| * Garbage collection |
| |
| Go is a garbage collected language |
| |
| But it's easy to limit heap allocations |
| |
| - many values are allocated on the stack |
| |
| - object pools: [[sync.Pool][http://golang.org/pkg/sync/#Pool]] |
| |
| - contiguous area of memory |
| |
| .play go4cpp/sizes.go /type/, |
| |
| * More about garbage collection |
| |
| Trusted in production. |
| |
| Brad Fitzpatrick's talk on migrating dl.google.com from C++ to Go: |
| |
| - [[https://talks.golang.org/2013/oscon-dl.slide#1][dl.google.com: Powered by Go]] |
| |
| Current state and road plan: |
| |
| - [[http://golang.org/s/go14gc][golang.org/s/go14gc]] |
| |
| * Inheritance vs Composition |
| |
| * Inheritance vs Composition |
| |
| - Inheritance breaks encapsulation |
| |
| - Composition causes boilerplate to proxy all methods |
| |
| Example: |
| |
| type Engine struct{} |
| |
| func (e Engine) Start() { ... } |
| func (e Engine) Stop() { ... } |
| |
| We want `Car` to be able to `Start` and `Stop` too. |
| |
| More detail in my talk [[http://talks.golang.org/2014/go4java.slide#32][Go for Javaneros]] |
| |
| * Struct embedding |
| |
| Composition + Proxy of selectors |
| |
| .play go4cpp/embedding.go /type/, |
| |
| * Struct embedding and the diamond problem |
| |
| What if two embedded fields have the same type? |
| |
| .play go4cpp/diamond.go /type/, |
| |
| * Struct embedding |
| |
| It looks like inheritance but _it_is_not_inheritance_. |
| |
| It is composition. |
| |
| Used to share implementations between different types. |
| |
| What if want to share behavior instead? |
| |
| * Interfaces |
| |
| * Interfaces |
| |
| An interface is a set of methods. |
| |
| In Java (C++ doesn't have interfaces) |
| |
| interface Switch { |
| void open(); |
| void close(); |
| } |
| |
| In Go: |
| |
| type OpenCloser interface { |
| Open() |
| Close() |
| } |
| |
| * It's all about satisfaction |
| |
| Java interfaces are satisfied *explicitly*. |
| |
| C++ abstract classes need to be extended *explicitly* |
| |
| Go interfaces are satisfied *implicitly*. |
| |
| .image //upload.wikimedia.org/wikipedia/commons/thumb/2/29/Rolling_Stones_09.jpg/512px-Rolling_Stones_09.jpg _ 512 |
| |
| .caption Picture by Gorupdebesanez [[http://creativecommons.org/licenses/by-sa/3.0][CC-BY-SA-3.0]], via [[http://commons.wikimedia.org/wiki/File%3ARolling_Stones_09.jpg][Wikimedia Commons]] |
| |
| * Go: implicit satisfaction |
| |
| _If_a_type_defines_all_the_methods_of_an_interface,_the_type_satisfies_that_interface._ |
| |
| Benefits: |
| |
| - fewer dependencies |
| - no type hierarchy |
| - organic composition |
| |
| * Structural subtyping |
| |
| Better than duck typing. Verified at compile time. |
| |
| .image go4cpp/duck.jpg 500 500 |
| |
| * FuncDraw: an example on interfaces |
| |
| .image go4cpp/funcdraw.png 500 700 |
| |
| * FuncDraw: package parser |
| |
| Package `parse` provides a parser of strings into functions. |
| |
| func Parse(text string) (*Func, error) { ... } |
| |
| `Func` is a struct type, with an `Eval` method. |
| |
| type Func struct { ... } |
| |
| func (p *Func) Eval(x float64) float64 { ... } |
| |
| * FuncDraw: package draw |
| |
| Package draw generates images given a function. |
| |
| func Draw(f *parser.Func) image.Image { |
| for x := start; x < end; x += inc { |
| y := f.Eval(x) |
| ... |
| } |
| } |
| |
| `draw` depends on `parser`, which makes testing hard. |
| |
| * Breaking dependencies |
| |
| Let's use an interface instead |
| |
| type Evaluable interface { |
| Eval(float64) float64 |
| } |
| |
| func Draw(f Evaluable) image.Image image.Image { |
| for x := start; x < end; x += inc { |
| y := f.Eval(x) |
| ... |
| } |
| } |
| |
| * Advanced interfaces and composition |
| |
| * Struct embedding of interfaces |
| |
| Embedding an interface: |
| |
| - more types can be used |
| - limits what is added to the embedding type |
| |
| Given: |
| |
| type Person struct { |
| First string |
| Last string |
| Age int |
| } |
| |
| `Employee` exposes the `Age` of `Person` |
| |
| type Employee struct { |
| Person |
| } |
| |
| e := Employee{Person{"John", "Doe", 49}} |
| |
| * Choosing what to proxy |
| |
| But we could hide it by choosing an interface: |
| |
| type Employee struct { |
| Namer |
| } |
| |
| type Namer interface { |
| Name() string |
| } |
| |
| And we need to make `Person` satisfy `Namer` |
| |
| func (e Person) Name() string { return e.First + e.Last } |
| |
| And the rest of the code still works: |
| |
| e := Employee{Person{"John", "Doe", 49}} |
| |
| * Easy mocking of interfaces |
| |
| Given this function: |
| |
| .code go4cpp/mock.go /func/,/^}/ |
| |
| How would you test it? |
| |
| * Easy mocking of interfaces |
| |
| `net.Conn` is an interface defined in the `net` package of the standard library. |
| |
| type Conn interface { |
| Read(b []byte) (n int, err error) |
| Write(b []byte) (n int, err error) |
| Close() error |
| LocalAddr() Addr |
| RemoteAddr() Addr |
| SetDeadline(t time.Time) error |
| SetReadDeadline(t time.Time) error |
| SetWriteDeadline(t time.Time) error |
| } |
| |
| We need a fake `net.Conn`! |
| |
| * One solution |
| |
| We could define a new type that satisfies `net.Conn` |
| |
| type fakeConn struct {} |
| |
| func (c fakeConn) Read(b []byte) (n int, err error) {...} |
| func (c fakeConn) Write(b []byte) (n int, err error) {...} |
| func (c fakeConn) Close() error {...} |
| ... |
| |
| But, is there a better way? |
| |
| * The better way |
| |
| .code go4cpp/mock.go /type fakeConn/,/end_fake/ |
| |
| And our test can look like: |
| |
| .play go4cpp/mock.go /func main/, |
| |
| * Concurrency |
| |
| * Concurrency |
| |
| It is part of the language, not a library. |
| |
| Based on three concepts: |
| |
| - goroutines: lightweight threads |
| - channels: typed pipes used to communicate and synchronize between goroutines |
| - select: control structure to coordinate concurrent operations |
| |
| .image go4cpp/funnelin.jpg 300 700 |
| |
| * Sleep and talk |
| |
| .code go4cpp/conc1.go /sleepAndTalk/,/^}/ |
| |
| We want a message per second. |
| |
| .play go4cpp/conc1.go /func main/,/^}/ |
| |
| What if we started all the `sleepAndTalk` concurrently? |
| |
| Just add `go`! |
| |
| * Concurrent sleep and talk |
| |
| .play go4cpp/conc2.go /func main/,/^}/ |
| |
| That was fast ... |
| |
| When the `main` goroutine ends, the program ends. |
| |
| * Concurrent sleep and talk with more sleeping |
| |
| .play go4cpp/conc3.go /func main/,/^}/ |
| |
| But synchronizing with `Sleep` is a bad idea. |
| |
| * Communicating through channels |
| |
| `sleepAndTalk` sends the string into the channel instead of printing it. |
| |
| .code go4cpp/chan.go /sleepAndTalk/,/^}/ |
| |
| We create the channel and pass it to `sleepAndTalk`, then wait for the values to be sent. |
| |
| .play go4cpp/chan.go /func main/,/^}/ |
| |
| * Aside: a web server |
| |
| A production ready web server. |
| |
| .play go4cpp/webserver.go |
| |
| * Let's count on the web |
| |
| Why is this wrong? |
| |
| .play go4cpp/badcounter.go /nextID/, |
| |
| * Let's count on the web correctly |
| |
| We receive the next id from a channel. |
| |
| .code go4cpp/goodcounter.go /nextID/,/^}/ |
| |
| We need to send ids into the channel. |
| |
| .code go4cpp/goodcounter.go /counter/,/^}/ |
| |
| * Let's count on the web correctly |
| |
| And we need to do both at the same time. |
| |
| .play go4cpp/goodcounter.go /func main/,/^}/ |
| |
| * Let's fight! |
| |
| `select` allows us to choose among multiple channel operations. |
| |
| .play go4cpp/battle.go /battle/,/^}/ |
| |
| Go - [[http://localhost:8080/fight?usr=go]] |
| C++ - [[http://localhost:8080/fight?usr=cpp]] |
| |
| * Chain of gophers |
| |
| .image go4cpp/chain.jpg |
| |
| Ok, I'm just bragging here |
| |
| * Chain of gophers |
| |
| .play go4cpp/goroutines.go /func f/, |
| |
| * Concurrency is very powerful |
| |
| And there's lots to learn! |
| |
| - [[http://talks.golang.org/2012/concurrency.slide#1][Go Concurrency Patterns]], by Rob Pike |
| - [[http://talks.golang.org/2013/advconc.slide#1][Advanced Concurrency Patterns]], by Sameer Ajmani |
| - [[http://talks.golang.org/2012/waza.slide#1][Concurrency is not Parellelism]], by Rob Pike |
| |
| .image go4cpp/busy.jpg |
| |
| * In conclusion |
| |
| - Go is simple, consistent, readable, and fun. |
| |
| - All types are equal: methods on any type. |
| |
| - Implicit satisfaction of interfaces makes code easier to reuse. |
| |
| - Use composition instead of inheritance. |
| |
| - Struct embedding is a powerful tool. |
| |
| - Concurrency is awesome, and you should check it out. |
| |
| * What to do next? |
| |
| Learn Go on your browser with [[http://tour.golang.org][tour.golang.org]] |
| |
| Find more about Go on [[http://golang.org][golang.org]] |
| |
| Join the community at [[https://groups.google.com/forum/#!forum/Golang-nuts][golang-nuts]] |
| |
| Link to the slides [[http://talks.golang.org/2015/go4cpp.slide]] |