Basics
Packages
- Every program is made up of packages.
"math/rand"package comprise files beginning withpackage rand- Use parenthesized, “factored” import statement to group multiple packages.
- In a package, a name is exported if it begins with a capital letter, like
PiorPrintln.- When importing a package, you can refer only to its exported names. Any “unexported” names are not accessible from outside the package.
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println(math.Pi)
}
Functions
- Type comes after the variable name.
- Shorten
x int, y intto `x, y int. - A function can return any number of results.
- Go’s return values may be named. If so, they are treated as variables defined at the top of the function.
func add(x int, y int) int {
return x + y
}
// any number of results
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
// named return values
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
// naked return, should be avoid in long functions
return
}
Data types
- The expression
T(v)converts the valuevto the typeT.
// basic
bool
string
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
g := 0.867 + 0.5i // complex128
// conversions
i := 42
f := float64(i)
u := uint(f)
Variables
varstatement can declare a list of variables.- Type is the last.
varstatement can be at package or function level.varcan have initializers.- Type can be omitted in this case.
:=for short assignment.varstatement can be “factored” into blocks.- Variables declared without an explicit initial value are given their zero value.
0for numeric types,falsefor boolean types,""empty string for strings
package main
import "fmt"
var c, python, java bool
func main() {
var i int
fmt.Println(i, c, python, java)
}
// initializer
var i, j int = 1, 2
var c, python, java = true, false, "no!"
// short assignment
k := 3
c, python, java := true, false, "no!"
// var block
var (
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)
Constants
- Constants are declared like variables, but with the const keyword.
- Constants can be character, string, boolean, or numeric values.
- Constants cannot be declared using the := syntax.
- Numeric constants are high-precision values.
- An untyped constant takes the type needed by its context.
const Pi = 3.14
const (
// Create a huge number by shifting a 1 bit left 100 places.
// In other words, the binary number that is 1 followed by 100 zeroes.
Big = 1 << 100 // works, longer than 64-bit
// Shift it right again 99 places, so we end up with 1<<1, or 2.
Small = Big >> 99
)
Control flows
For loops
- No parentheses around conditions.
- Braces
{}are required. forcan be used aswhile
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
// init and inc conditions can be empty
for ; sum < 1000; {
sum += sum
}
// while
sum := 1
for sum < 1000 {
sum += sum
}
If
ifcan start with a short statement to execute before the condition.- Variables declared here are only in scope until the end of
ifand related else.
- Variables declared here are only in scope until the end of
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("continue")
}
Switch
- Only run the selected case; thus
breakis not needed. - switch cases need not be constants or integers.
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
Defer
- A defer statement defers the execution of a function until the surrounding function returns.
- the function containing the defer statement
- Deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out order.
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
Advanced types
Pointers
*Tis a pointer to aTvalue.- Its zero value is
nil.
- Its zero value is
- No pointer arithmetic.
// declare a pointer
var p *int
// get pointer
i := 42
p = &i
// get the value
fmt.Println(*p)
// set the int value
*p = 21
Structs
structis a collection of fields.
// declare
type Vertex struct {
X int
Y int
}
// create an instance
v := Vertex{1, 2}
v2 := Vertex{X: 1} // 1, 0
v3 := Vertex{} // 0, 0
// set a field
v.X = 4
// get the pointer
p := &v
// set the field
(*p).X = 1
// or easier
p.X = 1
Arrays and slices
- Array cannot be resized.
[n]T - Slice is dynamically sized, flexible view into the element in an array.
[]T- Doesn’t store any data.
- Changing the elements of a slice modifies the corresponding elements of its underlying array.
- Zero value is
nil. - Can be created with
make - Can contain any type like other slices
func append(s []T, vs ...T) []T- Append values into a slice
- A bigger array will be allocated if needed.
// declare
var a [2]string
primes := [6]int{2, 3, 5, 7, 11, 13}
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
// slices, exclude the last one
var s [] int = primes[1:4] // 1, 2, 3
// create a slice
q := []int{2, 3, 5, 7, 11, 13}
a := make([]int, 5) // len(a) = 5
a := make([]int, 5, 10) // len(a) = 5, capacity is 10
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
// omit high or low bounds
var a [10]int
a[0:10]
a[:10]
a[0:]
a[:]
// slice append
var s []int
s = append(s, 2, 3, 4)
Range
rangeform of theforloop iterates over a slice or map.- For slice, two values for each iteration: index and element
- Skip one value by assigning
_
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
Maps
- A map maps keys to values.
- Zero value is
nil.
m := make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
fmt.Println(m["Bell Labs"])
//
var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
m[key] = elem
elem = m[key]
delete(m, key)
// test if a key is present
// use assign symbol if needed
elem, is_exist := m[key] // is_exist: bool
Functions
- Functions are values too. They can be passed around just like other values.
- May be used as function arguments and return values.
- Go functions may be closures. A closure is a function value that references variables from outside its body.
- The function may access and assign to the referenced variables; in this sense the function is “bound” to the variables.
func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
// closures
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
Methods and interfaces
Methods
- Go does not have classes; but we can define methods on types.
- A method is a function with a special receiver argument.
- Can only declare a method with a receiver whose type is defined in the same package as the method.
- Can declare methods with pointer receivers.
- Such methods can modify the value.
- Such methods can take either a value or pointer as the receiver.
- But functions can only take pointer as arguments.
- Methods with value argument can take either pointer or value.
- Functions must take values.
- Use a pointer receiver
- can modify the value.
- avoid copying the value on each call.
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
// equivalent to
fmt.Println(Abs(v))
}
// can define a new type and add methods
type MyFloat float64
// modify value
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
Interface
- An interface type is defined as a set of method signatures.
- A value of interface type can hold any value that implements those methods.
- No explicit declaration is needed, just implement those methods.
- Interface values stores the value and its concrete type.
describe(i)- Call corresponding methods of the concrete type.
- Handle
nilvalue in methods of a type. - Type switch
- Stringer
- Error
- Readers
- The
iopackage specifies theio.Readerinterface, which represents the read end of a stream of data. - Many implementations for different cases.
err == io.EOFwhen stream ends.
- The
type Abser interface {
Abs() float64
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser
// Error in the following line, v is a Vertex (not *Vertex)
// and does NOT implement Abser.
a = v
fmt.Println(a.Abs())
}
// empty interface can hold values of any type
interface{}
// type assertion, access to an interface value's underlying concrete value.
var i interface{} = "hello"
s := i.(string)
fmt.Println(s)
s, ok := i.(string)
fmt.Println(s, ok)
f, ok := i.(float64) // zero value, false
fmt.Println(f, ok)
f = i.(float64) // panic
fmt.Println(f)
// type switch
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
// Stringer interface, defined in fmt, can implement it to use Print
type Stringer interface {
String() string
}
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
// Error interface built in, can fmt Print it
type error interface {
Error() string
}
type MyError struct {
When time.Time
What string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
// Reader
func (T) Read(b []byte) (n int, err error)
func read() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}
Concurrency
Goroutines
go f(x, y, z)starts a new running goroutine, a lightweight thread.- goroutines run in the same address space, so access to shared memory must be synchronized.
syncpackage provides useful primitives.
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
go func() {
say("hello")
}()
}
Channels
- Channels are a typed conduit through which you can send and receive values with the channel operator,
<-.- The data flows in the direction of the arrow.
- Channels must be created before use.
- By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.
- Channels can be buffered. Provide the buffer length as the second argument to make to initialize a buffered channel.
- Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.
ch <- v // Send v to channel ch.
v := <-ch // Receive from ch, and assign value to v.
ch := make(chan int) // create a channel
// e.g.
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
// buffered channels
ch := make(chan int, 100)
- A sender can close a channel to indicate that no more values will be sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression.
v, ok := <-ch- Only sender can close the channel, never the receiver.
- The loop for
i := range creceives values from the channel repeatedly until it is closed. - The
defaultcase in aselectis run if no other case is ready.
Select
- The
selectstatement lets a goroutine wait on multiple communication operations. - A
selectblocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
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)
}
// default
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}
Mutex
- Mutual exclusion: But what if we don’t need communication? What if we just want to make sure only one goroutine can access a variable at a time to avoid conflicts?
sync.MutexprovidesLockandUnlock
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"))
}
Examples
Equivalent binary trees
package main
import "golang.org/x/tour/tree"
import "fmt"
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int, ) {
if (t == nil) {
return
}
Walk(t.Left, ch)
ch <- t.Value
Walk(t.Right, ch)
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
Walk(t1, ch1)
close(ch1)
}()
go func() {
Walk(t2, ch2)
close(ch2)
}()
for {
v1, ok1 := <- ch1
v2, ok2 := <- ch2
if (ok1 != ok2) {
return false
}
if (!ok1) {
return true
}
if (v1 != v2) {
return false
}
}
}
func main() {
res := Same(tree.New(1), tree.New(2))
fmt.Println(res)
}
