var foo int
var foo = 0
foo := 0
var a, b, c int = 1, 2, 3
var foo [5]int
var multiFoo [2][3]int
var foo []int
foo := []int{1, 2, 3, 4}
foo := make([]int, 10)
foo = append(foo, 5)
foo[2:] // 3, 4
foo[:3] // 1, 2, 3
var m map[string]int
m := make(map[string]int)
m["someKeyString"] = 0
Hint: Slices and Maps use internal pointers, so copies point to same underlying data
if foo := 9; foo < 0 {
fmt.Println(foo, "is negative") // not displayed
} else if foo < 10 {
fmt.Println(foo, "has 1 digit") // displayed
} else {
fmt.Println(foo, "has multiple digits") // not displayed
}
func foo(a int, b int) int {
c := a + b
return c
}
// Same thing:
func foo(a int, b int) (c int) {
c = a + b
return
}
import (
"fmt"
"strings"
)
func upCaseAll(str ...string) { // dynamic number of arguments
for _, s := range str { // range also returns key and value (use _ to forget about the key)
fmt.Println(strings.ToUpper(s))
}
}
func main() {
upCaseAll("Hello", "world")
upCaseAll("Hello")
aSlice := []string{"Hello", "World"}
upCaseAll(aSlice...)
}
type person struct {
name string
age int
gender string
}
p = person{name: "Thomas", age: 28, gender: "Male"}
p = person{"Thomas", 28, "Male}
p.name // Thomas
p.gender // Male
func main() {
resp, err := http.Get("http://example.com/")
if err != nil {
fmt.Println(err)
return
}
return fmt.Println(resp)
}
Go doesn't have classes. You have to define methods on types.
A method is a func
with a special receiver argument
type person struct {
name string
age int
}
func (p person) getName() string {
return p.name
}
func main() {
v := person{"Thomas", 28}
fmt.Println(v.getName()) // Prints Thomas
fmt.Println(v.name) // Prints Thomas too
}
Methods with pointer receivers can modify the value to which the receiver points.
In other languages you should have done (*p).name
for this to work
func (p *person) setName(s string) string {
p.name = s
return p.name
}
func main() {
v := person{"Thomas", 28}
fmt.Println(v.setName("Toto"))
fmt.Println(v.name) // Prints Toto instead of Thomas because of the pointer receiver which altered the name
}
They are implicitly implemented
type I interface {
aMethod()
}
type person struct {
name string
age int
}
func (p person) aMethod() {
fmt.Println(p.name)
}
func main() {
var p1 I = person{name: "Thomas", age: 28}
p1.aMethod()
fmt.Println(p1.name) // won't compile because person isn't accessible (p1 is an interface before all). Only aMethod (from the interface) is accessible
}
var i interface{} = "hello" // empty interface initialized with a string
s := i.(string)
fmt.Println(s) // displays hello as it's effectively a string
s, ok := i.(string) // here s = hello and ok = true
f, ok := i.(float64) // f = 0, ok = false
i, err := strconv.Atoi("42") // Some action
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)
It's basically a lightweight thread managed by the go runtime.
func main() {
go foo() // foo is executed in another thread
foo() // foo is executed in the main thread
}
It's a mechanism provided to send / receive values thanks to its operator <-
ch <- v
sends v value to channel ch
v := <- ch
receive from ch and assigns value to v
Create one channel of int with make(chan int)
func ping(pings chan<- string, msg string) { // pings can be used to send values (msg)
pings <- msg
}
func pong(pings <-chan string, pongs chan<- string) { // pings is used to receive values and pongs to send
msg := <-pings
pongs <- msg
}
Note the importance of <-
positions!
Hint: By default, channels are blocking. It means the channel blocks the sender until the receiver is available and the receiver is blocked until a message is available.
To go through this behavior, one can use buffered channels:
make(chan int, 50)
It will then block after 50 values instead of one. Might be useful for assymmetric loading ;)
A way to handle multiple channels. Use it inside a for-loop
select {
case v1 := <-c1:
fmt.Printf("received %v from channel 1\n", v1)
case v2 := <-c2:
fmt.Printf("received %v from channel 2\n", v2)
default:
fmt.Printf("No one was ready for communication\n")
}
select {
case v1 := <-c1:
fmt.Println("do smth")
case <-time.After(1 * time.Second):
fmt.Prinln("timeout after 1s")
}
Used to execute some code in the future once
timer1 := time.NewTimer(2 * time.Second)
<-timer1.C
fmt.Println("timer1 fired!")
timer2 := time.NewTimer(3 * time.Second)
go func() {
<-timer2.C
fmt.Println("timer2 fired!")
}()
stop2 := timer2.Stop()
if stop2 {
fmt.Println("timer2 stopped!")
}
Hint: Timers can be stopped contrary to sleeps!
Same as timers but for multiple times executions
ticker := time.NewTicker(500 * time.Millisecond)
// ...
go func() {
for {
select {
case <-done:
return
case t := <-ticker.C:
fmt.Println("Tick at", t)
}
}
}()
// ...
It's a function that returns a channel
func foo(msg string) <- string {
c := make(chan string)
go func() {
// ...
}()
return c
}
func main () {
// ...
f := foo("Thomas")
g := foo("Toto")
fmt.Println(<-f) // Here I can use the
fmt.Println(<-g) // channel to display its values
// ...
}
It aggregates multiple channels into one
func fanIn(a, b <-chan string) <-chan string {
c := make(chan string)
go func() { for { c <- <-a } }()
go func() { for { c <- <-b } }()
return c
}
func main() {
c:= fanIn(foo("Thomas"), foo("Toto"))
}
Or using select
(with only one goroutine)
func fanIn(a, b <-chan string) <-chan string {
c := make(chan string)
go func() {
for {
select {
case s := <-a:
c <- s
case s := <-b:
c <- s
}
}
}()
return c
}
The idea here is to wait for a bunch of routines to complete before doing something else:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Set the wait group to Done status after the worker is done
fmt.Printf("Worker %d starting..\n", id)
time.Sleep(time.Second) // faking process
fmt.Printf("Worker %d finished!\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i < 5; i++ {
wg.Add(1) // Adding 1 process to the wait group
go worker(i, &wg) // launch goroutine with the associated wait group
}
wg.Wait()
fmt.Println("All process are done!")
}