===== STUFF (tour) ===== * explain early that functions are not polymorphic (basics section) * earlier: type aliasing (which introduces a new type, not just an alias, except for byte and rune) * swap first two pages of Methods section * I would move closures later (possibly after methods) ===== Welcome ===== * "go tool tour" doesn't work on Fedora, why not? * offline tour runs ''${GOPATH}/bin/gotour'' * "package main" for commands ===== Packages ===== ==== Basic packages ==== * Every Go program is made up of packages, starts at ''main'' package. * By convention, the package name is the same as the last element of the import path. * ''import "math/rand"'' imports from ''math/rand/rand.go'': package rand import ( "math" ) * "factored" import statement: import ( "fmt" "math" ) ==== Exported stuff ==== * name is exported if it begins with a capital letter: func main() { fmt.Println(math.Pi) } * From ''math/const.go'': // Mathematical constants. const ( E = 2.71828182845904523536028747135266249775724709369995957496696763 // https://oeis.org/A001113 Pi = 3.14159265358979323846264338327950288419716939937510582097494459 // https://oeis.org/A000796 Phi = 1.61803398874989484820458683436563811772030917980576286213544862 // https://oeis.org/A001622 ... ) ===== Functions ===== ==== Basic functions ==== func add(x int, y int) int { return x + y } ==== Collapse return types ==== func add(x, y int) int { return x + y } ==== Multiple return values ==== 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 return } func main() { fmt.Println(split(17)) } Naked return statements should be used only in short functions, as with the example shown here. They can harm readability in longer functions. ===== Variables ===== ==== Types ==== 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 ==== Declarations ==== Type of list of variables is at the end: var c, python, java bool ==== Initializers ==== var i, j int = 1, 2 Inside functions, implicit short declarations using '':='': func main() { var i, j int = 1, 2 k := 3 c, python, java := true, false, "no!" fmt.Println(i, j, k, c, python, java) } Can be "factored" into blocks: package main import ( "fmt" "math/cmplx" ) var ( ToBe bool = false MaxInt uint64 = 1<<64 - 1 z complex128 = cmplx.Sqrt(-5 + 12i) ) Uninitialized variables are given their corresponding //zero value//: * 0 for numeric types, * false for the boolean type * "" (the empty string) for strings ==== Type conversions ==== No implicit type conversion, must be explicit: var i int = 42 var f float64 = float64(i) var u uint = uint(f) i := 42 f := float64(i) u := uint(f) Print the type of a variable: func main() { v := 42.1 // change me! fmt.Printf("v is of type %T\n", v) } ==== Constants ==== const Pi = 3.14 ===== Flow control ===== ==== for loop ==== Go's only looping construct: func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) } for ; sum < 1000; { sum += sum } Here's Go's while loop: for sum < 1000 { sum += sum } Go's infinite loop: for { } ==== if ==== func sqrt(x float64) string { if x < 0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) } Open with a short initializer statement: func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } return lim } Variables declared inside an if short statement are also available inside any of the else blocks. func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } else { fmt.Printf("%g >= %g\n", v, lim) } // can't use v here, though return lim } ==== switch ==== Go's switch cases need not be constants, and the values involved need not be integers. Top to bottom, executes only one possibility: func main() { fmt.Print("Go runs on ") 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) } } Switch with no condition same as ''switch true'' -- cleaner to write if-then-else: func main() { 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.") } } ==== defer ==== func main() { defer fmt.Println("world") fmt.Println("hello") } Multiple defers are stacked so executed in reverse order: func main() { fmt.Println("counting") for i := 0; i < 10; i++ { defer fmt.Println(i) } fmt.Println("done") } ===== Pointers ===== Go has no pointer arithmetic; zero value is ''nil''. Declaring simple pointers: var p *int Initialize at the same time with '':='': func main() { 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 } ===== Structs ===== ==== Structs ==== type Vertex struct { X int Y int } func main() { fmt.Println(Vertex{1, 2}) } ==== Struct fields ==== type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) } ==== Pointers to structs ==== type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} p := &v p.X = 1e9 // no need to dereference fmt.Println(v) } ==== Struct literals ==== 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 ) func main() { fmt.Println(v1, p, v2, v3) } ===== Arrays ===== Simple declaration (arrays cannot be resized): func main() { var a [2]string a[0] = "Hello" a[1] = "World" fmt.Println(a[0], a[1]) fmt.Println(a) primes := [6]int{2, 3, 5, 7, 11, 13} fmt.Println(primes) } ===== Slices ===== ==== Basic declaration ==== An array has a fixed size. A slice, on the other hand, is a dynamically-sized, flexible view into the elements of an array. In practice, slices are much more common than arrays. The type ''[]T'' is a slice with elements of type ''T'': func main() { primes := [6]int{2, 3, 5, 7, 11, 13} var s []int = primes[1:4] fmt.Println(s) } ==== Slices are simply references to arrays ==== A slice does not store any data, it just describes a section of an underlying array. Changing the elements of a slice modifies the corresponding elements of its underlying array. Other slices that share the same underlying array will see those changes. ==== Slice literals ==== func main() { q := []int{2, 3, 5, 7, 11, 13} fmt.Println(q) r := []bool{true, false, true, true, false, true} fmt.Println(r) s := []struct { i int b bool }{ {2, true}, {3, false}, {5, true}, {7, true}, {11, false}, {13, true}, } fmt.Println(s) } ==== Slice defaults ==== a[0:10] a[:10] a[0:] a[:] ==== Slice length and capacity ==== The length of a slice is the number of elements it contains. The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice. The length and capacity of a slice s can be obtained using the expressions ''len(s)'' and ''cap(s)''. ==== Nil slices ==== func main() { var s []int fmt.Println(s, len(s), cap(s)) if s == nil { fmt.Println("nil!") } } ==== Creating a slice with make ==== func main() { a := make([]int, 5) printSlice("a", a) b := make([]int, 0, 5) printSlice("b", b) c := b[:2] printSlice("c", c) d := c[2:5] printSlice("d", d) } ==== Slices of slices ==== func main() { // Create a tic-tac-toe board. board := [][]string{ []string{"_", "_", "_"}, []string{"_", "_", "_"}, []string{"_", "_", "_"}, } // The players take turns. board[0][0] = "X" board[2][2] = "O" board[1][2] = "X" board[1][0] = "O" board[0][2] = "X" for i := 0; i < len(board); i++ { fmt.Printf("%s\n", strings.Join(board[i], " ")) } } ==== Appending to a slice ==== ===== Range ===== var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} func main() { for i, v := range pow { fmt.Printf("2**%d = %d\n", i, v) } } func main() { pow := make([]int, 10) for i := range pow { pow[i] = 1 << uint(i) // == 2**i } for _, value := range pow { fmt.Printf("%d\n", value) } } ===== Maps ===== ===== Function closures (I would do this later) ===== func adder() func(int) int { sum := 0 // closure part 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), ) } } Fibonacci closure: func fibonacci() func() int { current, next := 0, 1 return func() int { current, next = next, next+current return current } } ===== Methods ===== ==== Non-method Abs() function ==== Non-method ''Abs()'' function for ''Vertex'', which takes a ''Vertex'' as a regular argument (no polymorphism) type Vertex struct { X, Y float64 } func Abs(v Vertex) float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := Vertex{3, 4} fmt.Println(Abs(v)) } ==== Basic method ==== Go does not support classes; rather, you can define methods on types by adding a //receiver argument// to a function: type Vertex struct { X, Y float64 } func (v Vertex) Abs() float64 { // receiver argument return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := Vertex{3, 4} fmt.Println(v.Abs()) // style 1 fmt.Println(Vertex{3,4}.Abs()) // style 2 } ==== Methods and type "aliasing" ==== You can declare a method on non-struct types, too. Below, we see a numeric type ''MyFloat'' with an ''Abs'' method: type MyFloat float64 // introduces a new type, not just an alias func (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f) } func main() { f := MyFloat(-math.Sqrt2) fmt.Println(f.Abs()) } You can only declare a method with a receiver whose type is defined in the same package as the method. You //cannot// declare a method with a receiver whose type is defined in another package (which includes the built-in types such as ''int''). ===== Methods with pointer receivers ===== ==== Refresher: call-by-value ==== Call-by-value function, which makes a //copy// of the argument: package main import ( "fmt" ) type Vertex struct { X, Y float64 } func Scale(v Vertex, f float64) Vertex { v.X = v.X * f v.Y = v.Y * f return v } func main() { v := Vertex{3, 4} fmt.Println(v) fmt.Println(Scale(v, 10)) fmt.Println(v) } {3 4} {30 40} {3 4} ==== Refresher: pointer-based function ==== Pointer-based call: package main import ( "fmt" ) type Vertex struct { X, Y float64 } func Scale(v *Vertex, f float64) { v.X = v.X * f v.Y = v.Y * f } func main() { v := Vertex{3, 4} fmt.Println(v) Scale(&v, 10) fmt.Println(v) } {3 4} {30 40} Notes about the above: * Even if the arg is a pointer, Go allows you to use "." to dereference. * You cannot support both variations of that function