| 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 | |
| - ... | |
| [[/wiki/GoUsers][go.dev/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 garbage 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: [[/pkg/sync/#Pool][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: | |
| - [[/talks/2013/oscon-dl.slide#1][dl.google.com: Powered by Go]] | |
| Current state and road plan: | |
| - [[/s/go14gc][go.dev/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 [[/talks/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! | |
| - [[/talks/2012/concurrency.slide#1][Go Concurrency Patterns]], by Rob Pike | |
| - [[/talks/2013/advconc.slide#1][Advanced Concurrency Patterns]], by Sameer Ajmani | |
| - [[/talks/2012/waza.slide#1][Concurrency is not Parallelism]], 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 [[/tour/][go.dev/tour]] | |
| Find more about Go on [[/][go.dev]] | |
| Join the community at [[https://groups.google.com/forum/#!forum/Golang-nuts][golang-nuts]] | |
| Link to the slides [[/talks/2015/go4cpp.slide]] |