Three machine dependent integer types: uint, int, uintptr They are machine dependent because their size depends on the type of architecture you are using.
Floating Point Numbers float32, float64 Generally we should stick with float64 when working with floating point numbers.
Complex Numbers complex64, complex128
Operators +, -, *, / and % are all the same as C.
Booleans
1
2
3
4
5
6
7
funcmain() {
fmt.Println(true && true)
fmt.Println(true && false)
fmt.Println(true || true)
fmt.Println(true || false)
fmt.Println(!true)
}
Strings
Go strings are made up of individual bytes, usually one for each character.
1
2
3
4
5
6
7
8
9
10
"Hello \nWorld"// Similar to C/C++
`Hello
World` // Similar to Python “””
funcmain() {
fmt.Println(len("Hello World"))
fmt.Println("Hello World"[1])
fmt.Println("Hello " + "World")
}
Characters from other languages like Chinese are represented by more than one byte.
1
2
3
4
5
6
7
8
9
10
11
12
funcmain() {
s := "abc汉字"
for i := 0; i < len(s); i++ { // byte
fmt.Printf("%c,", s[i])
}
fmt.Println()
for _, r := range s { // rune
fmt.Printf("%c,", r)
}
}
Output:
1
2
a,b,c,æ,±,,å,,,
a,b,c,汉,字,
Enum
1
2
3
4
5
6
7
8
9
10
11
12
13
// A Month specifies a month of the year (January = 1, ...).
type Month int
const (
January Month = 1 + iota
February
March
April
May
// ...
November
December
)
Variables
1
2
3
4
funcmain() {
var x string = "Hello World"
fmt.Println(x)
}
Shorter form:
1
x := "Hello World"
The type is not necessary because the Go compiler is able to infer the type based on the literal value you assign the variable. The compiler can also do inference with the var statement:
1
var x = "Hello World"
Generally you should use this shorter form whenever possible.
Scope
The same as C.
Constants
Constants are basically variables whose values cannot be changed later.
1
2
3
4
5
6
7
funcmain() {
const x string = "Hello World"
fmt.Println(x)
}
const x string = "Hello World"
x = "Some other string"// cannot assign to x
Constants must be numbers, strings and booleans which can be determined in compile time.
string index s[index] unicode, rune array/slice index s[index] map key m[key] channel element
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
s := "abc"
// 忽略 2nd value,支持 string/array/slice/map。
for i := range s {
fmt.Println(s[i])
}
for _, c := range s { // 忽略 index
fmt.Println(c)
}
m := map[string]int{"a": 1, "b": 2}
for k, v := range m { // 返回 (key, value)
fmt.Println(k, v)
}
Switch
分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。
1
2
3
4
5
6
7
8
9
10
11
x := []int{1, 2, 3}
i := 2
switch i {
case x[1]: // 不限于常量
fmt.Println("a")
case1, 3: // 多值匹配
fmt.Println("b")
default:
fmt.Println("c")
}
如需要继续下一分支,可使用 fallthrough,但不再判断条件。
1
2
3
4
5
6
7
8
9
x := 10
switch x {
case10:
fmt.Println("a")
fallthrough
case0:
fmt.Println("b")
}
Output:
1
2
a
b
省略条件表达式,可当 if…else if…else 使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
switch {
case x[1] > 0:
fmt.Println("a")
case x[1] < 0:
fmt.Println("b")
default:
fmt.Println("c")
}
switch i := x[2]; { // 带初始化语句
case i > 0:
fmt.Println("a")
case i < 0:
fmt.Println("b")
default:
fmt.Println("c")
}
Data Structures
Array
1
2
3
4
5
6
7
8
9
10
11
12
var a [4]int
fmt.Println(a) // [0 0 0 0]
fmt.Println(len(a)) // 4
fmt.Println(cap(a)) // 4
var a = [2]string{"Penn", "Teller"}
fmt.Println(a) // [Penn Teller]
fmt.Println(len(a)) // 2
var a = [...]string{"Penn", "Teller"}
fmt.Println(a) // [Penn Teller]
fmt.Println(len(a)) // 2
Similar to C but with differencies: Go’s arrays are values. An array variable denotes the entire array; it is not a pointer to the first array element (as would be the case in C).
Slice
The type specification for a slice:
1
[]T
Slices build on arrays to provide great power and convenience. Unlike an array type, a slice type has no specified length. Create with literal:
1
2
3
4
var s = []int{0, 1, 2, 3, 4}
fmt.Println(s) // [0 1 2 3 4]
fmt.Println(len(s)) // 5
fmt.Println(cap(s)) // 5
Create with built-in function make:
1
2
3
4
5
6
funcmake([]T, len, cap) []T
vars = make([]int, 5, 10)
fmt.Println(s) // [0 0 0 0 0]
fmt.Println(len(s)) // 5
fmt.Println(cap(s)) // 10
The zero value of a slice is nil.
1
2
3
4
var s []int
fmt.Println(s) // []
fmt.Println(s == nil) // true
fmt.Println(len(s)) // 0
Create by “slicing” an existing slice or array.
1
2
3
4
5
b := []byte{'g', 'o', 'l', 'a', 'n', 'g'}
// b[1:4] == []byte{'o', 'l', 'a'}
// b[:2] == []byte{'g', 'o'}
// b[2:] == []byte{'l', 'a', 'n', 'g'}
// b[:] == b
Create a slice given an array:
1
2
b := []byte{'g', 'o', 'l', 'a', 'n', 'g'}
s := b[:] // a slice referencing the storage of b
make([]byte, 5), is structured like this:
Re-slicing a slice doesn’t make a copy of the underlying array.
1
2
3
t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t
Grow a slice by built-in function append:
1
2
3
4
5
6
funcappend(s []T, x ...T) []T
a := make([]int, 1)
// a == []int{0}
a = append(a, 1, 2, 3)
// a == []int{0, 1, 2, 3}
Append one slice to another:
1
2
3
4
a := []string{"John", "Paul"}
b := []string{"George", "Ringo", "Pete"}
a = append(a, b...) // equivalent to "append(a, b[0], b[1], b[2])"
// a == []string{"John", "Paul", "George", "Ringo", "Pete"}
Map
A map is an unordered collection of key-value pairs.
1
var x map[string]int
x is a map of strings to ints.
Maps have to be initialized before they can be used.\
1
2
3
x := make(map[string]int)
x["10"] = 10
fmt.Println(x["10"])
Delete items from a map using the built-in delete function:
1
delete(x, “10”)
Looking up a key which doesn’t exist returns the zero value.
1
fmt.Println(x["unexisted"]) // 0
Similar to C++ std::map. Accessing an element of a map can return two values instead of just one.
The first value is the result of the lookup, The second tells us whether or not the lookup was successful.
In Go we often see code like this:
1
2
3
ifvalue, ok := x["0"]; ok {
fmt.Println(value, ok)
}
Shorter way to create maps:
1
2
3
4
5
x := map[string]int{
"1": 1,
"2": 2,
"3": 3,
}
Set
No build-in support.
1
map[X]struct{}
Functions
1
2
3
4
5
6
7
8
9
10
11
12
funcmain() {
xs := []float64{98, 93, 77, 82, 83}
fmt.Println(average(xs))
}
funcaverage(xs []float64)float64 {
total := 0.0
for _, v := range xs {
total += v
}
return total / float64(len(xs))
}
We can also name the return type:
1
2
3
4
5
6
7
8
funcaverage(xs []float64)(avg float64) {
total := 0.0
for _, v := range xs {
total += v
}
avg = total / float64(len(xs))
return
}
Returning Multiple Values
1
2
3
4
5
6
7
funcf()(int, int) {
return5, 6
}
funcmain() {
x, y := f()
}
Multiple values are often used to return an error value along with the result, or a boolean to indicate success. Here is a simple example, opening a file and reading some of it.
1
2
3
4
5
6
7
8
9
file, err := os.Open("file.go") // For read access.
if err != nil {
log.Fatal(err)
}
count, err := file.Read(data)
if err != nil {
log.Fatal(err)
}
Here’s the implementation of method Read:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
// EOF is signaled by a zero count with err set to io.EOF.
func(f *File)Read(b []byte)(n int, err error) {
if f == nil {
return0, ErrInvalid
}
n, e := f.read(b)
if n < 0 {
n = 0
}
if n == 0 && len(b) > 0 && e == nil {
return0, io.EOF
}
if e != nil {
err = &PathError{"read", f.name, e}
}
return n, err
}
Variadic Functions
There is a special form available for the last parameter in a Go function:
1
2
3
4
5
6
7
8
9
10
11
funcadd(args ...int)int { // Zero or more
total := 0
for _, v := range args {
total += v
}
return total
}
funcmain() {
fmt.Println(add(1,2,3))
}
This is precisely how the fmt.Println function is implemented:
1
funcPrintln(a ...interface{})(n int, err error)
We can also pass a slice of ints by following the slice with …:
1
2
3
4
funcmain() {
xs := []int{1,2,3}
fmt.Println(add(xs...))
}
Closure
It is possible to create functions inside of functions:
1
2
3
4
5
6
funcmain() {
add := func(x, y int)int {
return x + y
}
fmt.Println(add(1,1))
}
It also has access to other local variables:
1
2
3
4
5
6
7
8
9
funcmain() {
x := 0
increment := func()int {
x++
return x
}
fmt.Println(increment()) // 1
fmt.Println(increment()) // 2
}
A function like this together with the non-local variables it references is known as a closure.
Recursion
1
2
3
4
5
6
funcfactorial(x uint)uint {
if x == 0 {
return1
}
return x * factorial(x-1)
}
Defer, Panic & Recover
1
2
3
4
5
6
7
8
9
10
funcfirst() {
fmt.Println("1st")
}
funcsecond() {
fmt.Println("2nd")
}
funcmain() {
defer second()
first()
}
Defer is often used when resources need to be freed in some way.
1
2
f, _ := os.Open(filename)
defer f.Close()
Panic & Recover
1
2
3
4
5
6
7
funcmain() {
deferfunc() {
str := recover()
fmt.Println(str) // PANIC
}()
panic("PANIC")
}
A panic generally indicates a programmer error (for example,
attempting to access an index of an array that’s out of bounds forgetting to initialize a map etc.
) or an exceptional condition that there’s no easy way to recover from.
1
2
3
4
5
6
7
8
9
10
11
12
13
// pkg\bytes\buffer.go
// makeSlice allocates a slice of size n. If the allocation fails,
// it panics with ErrTooLarge.
funcmakeSlice(n int) []byte {
// If the make fails, give a known error.
deferfunc() {
ifrecover() != nil {
panic(ErrTooLarge)
}
}()
returnmake([]byte, n)
}
Pointers
Go is similar to C in pointers.
1
2
3
4
5
6
7
8
9
funczero(xPtr *int) { // pointer
*xPtr = 0// dereference
}
funcmain() {
x := 5
zero(&x) // address
fmt.Println(x) // 0
}
Build-in function “new”
Function “new” takes a type as an argument, allocates enough memory to fit a value of that type and returns a pointer to it.
1
2
3
4
5
6
7
8
9
funcone(xPtr *int) {
*xPtr = 1
}
funcmain() {
xPtr := new(int)
one(xPtr)
fmt.Println(*xPtr) // 1
}
Pointers are rarely used with Go’s built-in types. They are extremely useful when paired with structs.
Structs
A struct is a type which contains named fields.
1
2
3
4
5
type Circle struct {
x float64
y float64
r float64
}
The fields with the same type can be collapsed:
1
2
3
typeCirclestruct {
x, y, r float64
}
Initialization
Create a local variable with zero initialization:
1
varcCircle// {0 0 0}
Use new function:
1
c := new(Circle) // Also {0 0 0}
This allocates memory for all the fields, sets each of them to their zero value and returns a pointer (*Circle). More often we want to give each of the fields a value.
1
c := Circle{x:0, y:0, r:5}
Or,
1
c := Circle{0, 0, 5}
Note that variable c is NOT a pointer of Circle.
1
2
3
4
5
6
7
8
9
10
11
funczeroCircle(c *Circle) {
c.x = 0// No -> operator like C! Just use dot.
c.y = 0
c.r = 0
}
funcmain() {
c := Circle{x: 0, y: 0, r: 5}
zeroCircle(&c) // c is not a pointer, have to address it.
fmt.Println(c) // {0 0 0}
}
Constructor?
没有构造和析构方法,通常用简单工厂模式返回对象实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
type Queue struct {
elements []interface{}
}
funcNewQueue() *Queue {
return &Queue{make([]interface{}, 10)}
}
// src\pkg\container\list\list.go
package list
// New returns an initialized list.
funcNew() *List { returnnew(List).Init() }
Methods
Normal function:
1
2
3
funccircleArea(c *Circle) float64 {
return math.Pi * c.r*c.r
}
Method:
1
2
3
func(c *Circle) area() float64 { // Receiver
return math.Pi * c.r*c.r
}
Call the method:
1
fmt.Println(c.area())
Interfaces
Similarily, define struct Rectangle:
1
2
3
4
5
6
7
8
9
type Rectangle struct {
x1, y1, x2, y2 float64
}
func(r *Rectangle)area()float64 {
l := distance(r.x1, r.y1, r.x1, r.y2)
w := distance(r.x1, r.y1, r.x2, r.y1)
return l * w
}
Both Circle and Rectangle have a method named area(). Let’s define an Interface for this similarity:
1
2
3
type Shape interface {
area() float64
}
Instead of defining fields, interface defines a “method set”: a list of methods that a type must have in order to “implement” the interface. Function taking interface types as arguments:
1
2
3
4
5
6
7
functotalArea(shapes ...Shape)float64 {
var area float64
for _, s := range shapes {
area += s.area()
}
return area
}
Now call it:
1
fmt.Println(totalArea(&c, &r))
Interfaces can also be used as fields:
1
2
3
typeMultiShapestruct {
shapes []Shape
}
We can even turn MultiShape itself into a Shape by giving it an area method: