- A tour of go
- The little go book
- How to write go code
- Writting web applications
- Effective Go
- Go Code Review Comments
- Testing techniques
- Go Printing reference
// Declaration
var a int
a = 42
// Shortcut with type infering
b := 42
To be placed after import
const A = 3
const (
timeout = 0.75
url = "https://github.com/"
flag = true
)
// false is Zero value for bools
bool
// "" is Zero value for string
string
// 0 is the Zero value for numbers
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32
// represents a Unicode code point
float32 float64
complex64 complex128
Type conversions:
i := 42
f := float64(i)
u := uint(f)
// Arrays (fixed size)
var a [2]string
a[0] = "Hello"
a[1] = "World"
primes := [6]int{2, 3, 5, 7, 11, 13}
// slices (dynamic size), refereces of an underlying array which can be explicit or implicit
s := primes[1:4]
s[0] = 99
fmt.Println(primes)
s := []struct {
i int
b bool
}{
{1, true},
{2, true},
{3, false},
}
// len of the slice
len(s) // 3
// capacity of the underlying array
cap(s) // 3
s = s[:1]
s[3] // would throw 'index out of range'
s = s[1:5] // We can resize the array
s := make([]int, 5) // Creates an slice of Zero elements with len 5
s := make([]int, 0, 5) // Creates an slice of 0 len an 5 cap
// Append will add elements to the slice even if excees the unerlying array capacity
var a []int
a = append(a, 3)
var m0 = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
// Type name can be omitted
m0 = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
fmt.Println(m0)
m := make(map[string]int)
// map operations
m["Answer"] = 42
fmt.Println("The value:", m["Answer"])
m["Answer"] = 48
fmt.Println("The value:", m["Answer"])
delete(m, "Answer")
fmt.Println("The value:", m["Answer"])
v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok)
Conditionals:
if _, err := someFunct(); err != nil {
// handle error
}
if someCondition {
// ...
} else if someOtherCondition {
// ...
} else {
// ...
}
Loops:
for i := 0; i < N; i++ { // any of the three separated by ; is optional
// "regular" loop
}
for {
// infinite loop
}
for condition {
// like while in other languages
}
// Iter slices
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
fmt.Printf("%v^2 = %v", i, v)
}
for i := range pow {
// Only iter indexes
}
for _, v := range pow {
// Skip indexes
}
// Iter maps
m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
for k, v := range(m) {
fmt.Printf("%v, coordinates: %v", k, v)
}
for k := range(m) {
// Only iter keys
}
for _, v := range(m) {
// Only values
}
type Vertex struct {
X, Y int
}
var (
v1 = Vertex{1, 2} // has type Vertex
v2 = Vertex{X: 1} // Y:0 is implicit
v3 = Vertex{} // X:0 and Y:0
p = &Vertex{1, 2} // has type *Vertex
)
// Copy a struct (or any other variable)
copy := v1
i, j := 42, 2701
p := &i // point to i
fmt.Println(*p) // read i through the pointer
*p = 21 // set i through the pointer
fmt.Println(i) // see the new value of i
p = &j // point to j
*p = *p / 37 // divide j through the pointer
fmt.Println(j) // see the new value of j
v := Vertex{1, 2}
q := &v // point to v
q.X = 10 // set a struct field directly (no need to do (*q).X)
fmt.Println(v)
type MyFloat float64
// Abs for custom float
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
// Usually methods are defined with pointers receivers as then, it is possible to modify the value
type Vertex struct {
X, Y float64
}
func (v *Verteex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
// The equivalent function
func ScaleFunc(v *Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
v := Vertex(3, 4)
v.Scale(2) // This is syntaxis-sugar for (&v).Scale(2)
ScaleFunc(&v, 2) // this would be the equivalent
v := &Vertex{3, 4}
v.Scale(2) // This work the same
// Method using valure receiver
func (v Vertex) Abs() float64 {
return mat.Sqrt(v.X*v.X + v.Y*v.Y)
}
v := Vertex{3, 4}
v.Abs()
v := &Vertex{3, 4}
v.Abs() // syntaxic-sugar for (*v).Abs()
An interface is a definition of signature (without implementation), the interface implementation is implicit (a type implements an interface if it defines all its methods)
type I interface {
M()
}
type T struct {
S string
}
type F float64
func (t T) M() {
fmt.Println(t.s)
}
func (f F) M() {
fmt.Println(f)
}
// Both types implement the interface I
func use(i I) {
i.M()
}
use(F(42))
use(I{"Meaning"})
Methods can handle nil values
func (t *T) Show() {
if t == nil {
fmt.Println("Nope")
} else {
}
}
The empty interface describes any type interface{}
func describe(any interface{}) {
fmt.Printf("(%v, %T\n)", any, any)
}
We can go from the interface to its type using type assertions
var nothing interface{}
nothing = "hello"
s := nothing.(string)
nothing = 42
i := nothing.(int)
x := nothing.(string) // panic (conversion)
nothing = 1.4
// Type switch
switch v := empty.(type) {
case int:
fmt.Println("Is int!")
case string:
fmt.Println("Is string")
default:
fmt.Println("Is other thing...") // will be here
}
Stringers implement the string method
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
me := Person{"Christian", 32}
fmt.Println(me) // Will print "Christian (32 years)"
Errors interface is similar to Stringers
type MyError struct {
When time.Time
What string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}
if err := run(); err != nil {
fmt.Println(err)
}
// ReadWriter implementations must satisfy both Reader and Writer
type ReadWriter interface {
Reader
Writer
}
// Server exposes all the methods that Logger has
type Server struct {
Host string
Port int
*log.Logger
}
Goroutines are light threads (not OS threads, but routines managed by Go). To start a new goroutine it is just needed to include go
before a function call. Example: go f(a)
.
Also an anonymous function may be used:
go func (a int) {
// do stuff
}(42)
package main
import (
"fmt"
"sync"
"time"
)
// SafeCounter is safe to use concurrently.
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}
// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.v[key]++
c.mux.Unlock()
}
// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mux.Unlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}
The sync
package also includes RWMutex which allows to only lock read and/or write operations.
Channels are concurrency-safe communication objects. See more info in the channels section of the tutorial.
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to channel
}
func main() {
s := []int{7, 2, 8, -9, 4, 8}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from channel
}
Buffered chanells have length (length can be tested with cap
function)
c := make(chan int, 2)
c <- 1
c <- 2
// Will get an error if overfill the channel
// c <- 3 // fatal error: all goroutines are asleep - deadlock
Sender can close channels to tell no more values are comming. Receivers can test if a channel is closed using the second parameter.
Receiving from a closed channels returns the zero value inmediately.
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonnaci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
select
lets a goroutine wait on multiple communication operations. It blocks until one of the cases can run and choses ramdonly if multiple can run.
func fibonacci(c, quit chan it) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <- quit:
fmt.Println("I'm done!")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println( <- c)
}
quit <- 0
}()
fibonacci(c, quit)
}
A default case in slect let us try a send/receive without blocking
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisencond)
for {
select {
case <- tick:
fmt.Println("tick.")
case <- boom:
fmt.Printl("BOOM!")
return
default:
fmt.Println(".")
time.Sleep(50 * time.Millisecond)
}
}
}
The Context
type carries deadlines, cancelation signals and other request-scoped values across API boundaries and between processes. See:
See Context exercise.
Test files live next to the files containing the code that are testing:
foo.go
foo_test.go
The naming is a required pattern in Go.
Test (always included in *_test.go
files) must be named Test<name>(* testing.T)
.
package main
import "testing"
func TestSimple(t *testing.T) {
if true {
t.Error("expected false, got true")
}
}
*testing.T
type has method available to control the tests flow. See testing docs.
To avoid circular dependency imports go let's us create a "magic" testing package.
// foo.go
package foo
// foo_test.go
package foo_test
It is optional and is an exception to "one package per folder" requirement. It helps with circular dependencies but, as it is a new package, all rules about exporting names apply.
go test <package-path> # run tests of package in path
go test ./... # run all test in packages recursively
go test -v . # run test in current package in verbose mode
go test -run "Call" -v ./... # run all test maching the "Call" regular expression
go test -race race_test.go # run race_test.go checkin race conditions
# For all tests
go test -coverprofile cover.out
go tool cover -html=cover.out
# For specific test
go test -coverprofile cover.out -run TestUser_Validate
go tool cover -html=cover.out
See test exercise.
To be able to run some exercises it is needed to install the tour in the GOPATH
go get golang.org/x/tour/gotour
At http://hamlet.gopherguides.com you will find a text copy of the play Hamlet.
Write a program using contexts that searches each line of text from the play to search for a particular word. Examples are Hamlet, Mark, King etc...
The program must:
print out the line number the word was found, and the text of that line:
3545: QUEEN GERTRUDE O Hamlet, speak no more
stop when it finds 50 occurrences of the word stop after 5 seconds if it hasn't yet found 50 occurrences