blob: 1af3d845095e07f7896e439a9a6c6ccb39222c4d [file] [log] [blame]
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]]