===== 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