nil
*
to obintain value, &
to get address of a variablei, j := 42, 2701
p := &i // point to i, or =var p *int = &i=
*p = 21
p = &j
*p = *p / 37
(*p).X -> p.X
v = Vertex{X: 1}
type Vertex struct {
X int
Y int
}
func main() {
v := &Vertex{1, 2}
v.X = 4
}
var a[10]int
declare an int arrayvar a[2]string
a[0] = "Hello"
a[1] = "World"
primes := [6]int{2, 3, 5, 7, 11, 13}
// or primes := [...]int{2, 3, 5, 7, 11, 13}
zero value is nil
declare: letters := []string{"a", "b", "c", "d"}
can created by func make([]T, len, cap) []T
, cap
is optional, default same with len
var s []byte
s = make([]byte, 5, 5)
// s == []byte{0, 0, 0, 0, 0}
slice can be “slicing” of a slice or an array: a[1:3]
, half open
a slice doesn’t store data, only a reference to orginal array, change data will affect array
capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s) // len=6 cap=6 [2 3 5 7 11 13]
// Slice the slice to give it zero length.
s = s[:0]
printSlice(s) // len=0 cap=6 []
// Extend its length.
s = s[1:4]
printSlice(s) // len=3 cap=5 [3 5 7]
// Drop its first two values.
s = s[2:]
printSlice(s) // len=1 cap=3 [7]
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
Growing capacity
manually
t := make([]byte, len(s), (cap(s)+1)*2) // +1 in case cap(s) == 0
for i := range s {
t[i] = s[i]
}
s = t
use func copy(dst, src []T) int
t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t
Append data
use func append(s []T, x ...T) []T
append
adds data to a slice and increase capacity if necessary
a := make([]int, 1) // a == []int{0}
a = append(a, 1, 2, 3) // a == []int{0, 1, 2, 3}
aa := []string{"John", "Paul"}
b := []string{"George", "Ringo", "Pete"}
aa = append(aa, b...) // equivalent to "append(aa, b[0], b[1], b[2])"
// aa == []string{"John", "Paul", "George", "Ringo", "Pete"}
Some usecases
use in passing arguments, to avoid coping whole array
filter, declare an empty slice and append data to it
// Filter returns a new slice holding only the elements of s that satisfy fn()
func Filter(s []int, fn func(int) bool) []int {
var p []int // == nil
for _, v := range s {
if fn(v) {
p = append(p, v)
}
}
return p
}
replace array
to save memory
func CopyDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
c := []byte
c = append(c, digitRegexp.Find(b))
return c
}
If just return digitRegexp.Find(b)
, the array b
will not be released.
iterate over a slice
or map
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
v += 1
fmt.Printf("2**%d = %d\n", i, v)
}
map keys to values
nil
, no keys and nor can keys be addeduse make
to create a map
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
fmt.Println(m["Bell Labs"])
}
or use literal to create a map
var m = map[string]Vertex{
"Bell Labs": Vertex{40.68433, -74.39967,},
"Google": {37.42202, -122.08408,},
}
func main() {
m["1"] = Vertex{1, 2}
fmt.Println(m)
}
m[key] = elem
elem = m[key]
, if not exist, return key type’s zero valueelem, exist := m[key]
, if exist, exist==true
delete(m, key)
functions are also values which can be passed as arguments or assined to variables
func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(5, 12))
fmt.Println(compute(hypot))
fmt.Println(compute(math.Pow))
}
A closure is a function value that references variables from outside its body
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),
)
}
}
type NewType float64
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())
}
This is equal to:
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
Use pointer receivers *T
passing reference of the same object to functions to directly modify the same object
Methods with pointer receivers can eithered be called with a variable or a pointer
var v Vertex
v.Scale(5) // OK
p := &v
p.Scale(10) // OK
Methods with value receivers also take either a value or a pointer
(value, type)
, holding a value of a specific concrete typetype I interface {
M()
}
type T struct {
S string
}
// This method means type T implements the interface I,
// but we don't need to explicitly declare that it does so.
func (t T) M() {
fmt.Println(t.S)
}
func main() {
var i I = T{"hello"}
i.M()
}
Interface value can have nil underlying value and can call methods, concrete type’s nil value can not
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func main() {
var i I
var t *T
i = t
i.M() // output: <nil>
// T.M() // compile error
}
Call a nil interface value will cause runtime error
Empty Interface An empty interface can hold values of any type Usually used where need to handle values of unknown type
func main() {
var i interface{}
describe(i)
i = 42
describe(i)
i = "hello"
describe(i)
}
func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}
Type assertions
t := i.(T)
i
holds a T
, return that valuet, ok := i.(T)
i
holds a T
, return that value and trueType 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)
}
}
func main() {
do(21)
do("hello")
do(true)
}
defined by fmt
package
type Stringer interface {
String() string
}
type implements String
method can print custom strings by fmt.Println
error
is a built-in interfacetype error interface {
Error() string
}
error
denotes success; non-nil denotes failuretype ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %f", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
...
return z, nil
}
io.Reader
interface has a Read
method:
func (T) Read(b []byte) (n int, err error)
it will return io.EOF
when stream ends
A goroutine is a lightweight thread, running in the same address space
go f(x, y, z)
starts a new goroutine
Like pipe, receive and send values use channel operator <-
Created before use: ch := make(chan int, <buffer size>)
By default, receive and send block until the other side is ready usually used to sync routines
// get sum of a array using two routines
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)
}
Channel can be closed by sender
Receiver can get channel status by v, ok := <- ch
Loop for i:= range c
receives values until c
is closed
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 fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
Select
lets a goroutine execute a case which is ready, or pick one randomly if all cases are ready
if default
case exists, Select
will execute it when no channels 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)
}
Lock
and Unlock
in sync.Mutex
// 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"))
}
{}
is always requiredfor i := 0; i < 10; i++ {
sum += i
}
for ; sum < 1000; {
sum += sum
}
for sum < 1000 {
sum += sum
}
for {
}
{}
is always requiredif v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
break
is not needed in each case
case
can be any valuecase
evaluated from top to bottomswitch true
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd, plan9, windows...
fmt.Printf("%s.\n", os)
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
func main() {
defer fmt.Println("2. world")
defer fmt.Println("1. ")
fmt.Println("0. hello")
}
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i) // print as 9\n 8\n ...., 0
}
fmt.Println("done")
}