go_tour_notes

  • 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)
  • “go tool tour” doesn't work on Fedora, why not?
  • offline tour runs ${GOPATH}/bin/gotour
  • “package main” for commands

* 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"
)

* 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
        ...
       )
func add(x int, y int) int {
	return x + y
}
func add(x, y int) int {
	return x + y
}
func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	a, b := swap("hello", "world")
	fmt.Println(a, b)
}
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.

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

Type of list of variables is at the end:

var c, python, java bool
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

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)
}
const Pi = 3.14

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 {
	}
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
}

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.")
	}
}
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")
}

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
}
type Vertex struct {
	X int
	Y int
}

func main() {
	fmt.Println(Vertex{1, 2})
}
type Vertex struct {
	X int
	Y int
}

func main() {
	v := Vertex{1, 2}
	v.X = 4
	fmt.Println(v.X)
}
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)
}
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)
}

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)
}

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)
}

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.

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)
}
a[0:10]
a[:10]
a[0:]
a[:]

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).

func main() {
	var s []int
	fmt.Println(s, len(s), cap(s))
	if s == nil {
		fmt.Println("nil!")
	}
}
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)
}
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], " "))
	}
}
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)
	}
}
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
    }
}

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))
}

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
}

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).

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}

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
  • go_tour_notes.txt
  • Last modified: 2018/04/14 08:57
  • by rpjday