Packages, variables, and function

Packages

Every Go program is made up of packages.

Programs start running in package main.

This program is using the packages with import paths "fmt" and "math/rand".

By convention, the package name is the same as the last element of the import path. For instance, the "math/rand" package comprises files that begin with the statement package rand.

Note: the environment in which these programs are executed is deterministic, so rand.Intn will always return the same number. (To see a different number, seed the number generator; see rand.Seed.)

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is ", rand.Intn(10))
    //fmt.Println(rand.Seed(1))
}

Imports

This code groups the imports into a parenthesized, “factored” import statement.

You can also write multiple import statements, like:

import "fmt"
import "math"

But it is good style to use the factored import statement.

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Printf("Now you have %g problems.", math.Nextafter(2, 3))
}

Exported names

After importing a package, you can refer to the names it exports.

In Go, a name is exported if it begins with a capital letter.

Foo is an exported name, as is FOO. The name foo is not exported.

Run the code. Then rename math.pi to math.Pi and try it again.

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Println(math.Pi)
}

Functions

A function can take zero or more arguments.

In this example, add takes two parameters of type int.

Notice that the type comes after the variable name.

package main

import "fmt"

func add(x int, y int) int {
    return x + y
}

func main() {
    fmt.Println(add(42, 13))
}

Functions continued

When two or more consecutive named function parameters share a type, you can omit the type from all but the last.

In this example, we shortened

x int, y int

to

x, y int
package main

import "fmt"

func add(x, y int) int {
    return x + y
}

func main() {
    fmt.Println(add(42, 13))
}

Multiple results

A function can return any number of results.

The swap function returns two strings.

package main

import "fmt"

func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	a, b := swap("hello", "world")
    fmt.Println(a, b)
}

Named return values

Go’s return values may be named and act just like variables.

These names should be used to document the meaning of the return values.

A return statement without arguments returns the current values of the results. This is known as a “naked” return.

Naked return statements should be used only in short functions, as with the example shown here. They can harm readability in longer functions

package main

import "fmt"

func split(sum int) (x, y int) {
	x = sum * 4 / 9
	y = sum - x
	return
}

func foo() (x, y, z int) {
	x = 1
	y = 2
	z = 3
	return
}

func main() {
	fmt.Println(split(17))
	fmt.Println(foo())
}

Variables

The var statement declares a list of variables; as in function argument lists, the type is last.

A var statement can be at package or function level. We see both in this example.

package main

import "fmt"

var c, python, java bool

func main() {
    var i int
    fmt.Println(i, c, python, java)
}

Variables with initializers

A var declaration can include initializers, one per variable.

If an initializer is present, the type can be omitted; the variable will take the type of the initializer.

package main

import "fmt"

var i, j = 1, 2
var x, y = "hello", 100

func main() {
	var c, python, java = true, false, "no!"
	fmt.Println(i, j, c, python, java)
	fmt.Println(x, y)
}

Short variable declarations#

Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type.

Outside a function, every statement begins with a keyword (var, func, and so on) and so the := construct is not available.

package main

import "fmt"

func main() {
	var i, j int = 1, 2
	k := 3
	c, python, java := true, false, "no!"

	fmt.Println(i, j, k, c, python, java)
}

Basic types

Go’s basic types are

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

The example shows variables of several types, and also that variable declarations may be “factored” into blocks, as with import statements.

The int, uint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. When you need an integer value you should use int unless you have a specific reason to use a sized or unsigned integer type.

package main

import (
	"fmt"
	"math/cmplx"
)

var (
	ToBe   bool       = false
	MaxInt uint64     = 1<<64 - 1
	z      complex128 = cmplx.Sqrt(-5 + 12i)
)

func main() {
	const f = "%T(%v)\n"
	fmt.Printf(f, ToBe, ToBe)
	fmt.Printf(f, MaxInt, MaxInt)
	fmt.Printf(f, z, z)
	fmt.Printf(f, f)
}

Zero values

Variables declared without an explicit initial value are given their zero value.

The zero value is:

0 for numeric types, false the boolean type, and “” (the empty string) for strings.

package main

import "fmt"

func main() {
    var i int
    var f float64
    var b bool
    var s string
    fmt.Printf("%v %v %v %v\n", i, f, b, s)
}

Type conversions

The expression T(v) converts the value v to the type T.

Some numeric conversions:

var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

Or, put more simply:

i := 42
f := float64(i)
u := uint(f)

Unlike in C, in Go assignment between items of different type requires an explicit conversion. Try removing the float64 or int conversions in the example and see what happens.

package main

import (
	"fmt"
	"math"
)

func main() {
	var x, y int = 3, 4
	var f float64 = math.Sqrt(float64(x*x + y*y))
	var z int = int(f)
	fmt.Println(z, y, z)
}

Type inference

When declaring a variable without specifying an explicit type (either by using the := syntax or var = expression syntax), the variable’s type is inferred from the value on the right hand side.

When the right hand side of the declaration is typed, the new variable is of that same type:

var i int
j := i // j is an int

But when the right hand side contains an untyped numeric constant, the new variable may be an int, float64, or complex128 depending on the precision of the constant:

i := 42           // int
f := 3.142        // float64
g := 0.867 + 0.5i // complex128

Try changing the initial value of v in the example code and observe how its type is affected.

package main

import "fmt"

func main() {
	v := "pgg" // change me!
	fmt.Printf("v is of type %T\n", v)
}

Constants

Constants are declared like variables, but with the const keyword.

Constants can be character, string, boolean, or numeric values.

Constants cannot be declared using the := syntax.

package main

import "fmt"

const Pi = 3.14

func main() {
	const World = "世界"
	fmt.Println("Hello", World)
	fmt.Println("Happy", Pi, "Day")

	const Truth = true
	fmt.Println("Go rules?", Truth)
}

Numeric Constants

Numeric constants are high-precision values.

An untyped constant takes the type needed by its context.

Try printing needInt(Big) too.

package main

import "fmt"

const (
	Big   = 1 << 100
	Small = Big >> 99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
	return x * 0.1
}

func main() {
	fmt.Println(needInt(Small))
	fmt.Println(needFloat(Small))
	fmt.Println(needFloat(Big))
	fmt.Println(needInt(int(Big)))
}

Flow control statements: for, if, else, switch and defe

For

Go has only one looping construct, the for loop.

The basic for loop looks as it does in C or Java, except that the ( ) are gone (they are not even optional) and the { } are required.

package main

import "fmt"

func main() {
	sum := 0
	for i := 0; i < 10; i++ {
		sum += 1
	}
	fmt.Println(sum)
}

For continued

As in C or Java, you can leave the pre and post statements empty.

package main

import "fmt"

func main() {
	sum := 1
	for ; sum < 1000 ; {
		sum += sum
	}
	fmt.Println(sum)
}

For is Go’s “while”

At that point you can drop the semicolons: C’s while is spelled for in Go.

package main

import "fmt"

func main() {
	sum := 1
	for sum < 1000 {
		sum += sum
	}
	fmt.Println(sum)
}

Forever

If you omit the loop condition it loops forever, so an infinite loop is compactly expressed.

package main

import "fmt"

func main() {
	var i int = 10
	for {
		fmt.Println("Hello TOM!")
		i = i - 1
		if i < 0 {
			break
		}
	}
}

If

The if statement looks as it does in C or Java, except that the ( ) are gone and the { } are required.

(Sound familiar?)

package main

import (
	"fmt"
	"math"
)

func sqrt(x float64) string {
	if x < 0 {
		return sqrt(-x) + "i"
	}
	return fmt.Sprint(math.Sqrt(x))
}

func main() {
	fmt.Println(sqrt(2), sqrt(-4))
}

If with a short statement

Like for, the if statement can start with a short statement to execute before the condition.

Variables declared by the statement are only in scope until the end of the if.

(Try using v in the last return statement.)

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	v := 1
	if v := math.Pow(x, n); v < lim {
		fmt.Println(v)
		return v
	}
	fmt.Println(v)
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20))
}

If and else

Variables declared inside an if short statement are also available inside any of the else blocks.

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	} else {
		x := 100


		fmt.Println(x)
		fmt.Printf("%g >= %g\n", v, lim)
	}
	// can't use v here, though
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

Switch

You probably knew what switch was going to look like.

A case body breaks automatically, unless it ends with a fallthrough statement.

package main

import (
	"fmt"
	"runtime"
)

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.Print("%s.", os)
	}
}

Switch evaluation order

Switch cases evaluate cases from top to bottom, stopping when a case succeeds.

(For example,

switch i {
case 0:
case f():
}

does not call f if i==0.)

Note: Time in the Go playground always appears to start at 2009-11-10 23:00:00 UTC, a value whose significance is left as an exercise for the reader.

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("When's Saturday?")
	today := time.Now().Weekday()
	switch time.Saturday {
	case today + 0:
		fmt.Println("Today.")
	case today + 1:
		fmt.Println("Tomorrow.")
	case today + 2:
		fmt.Println("In two days.")
	default:
		fmt.Println("Too far away.")
	}
}

Switch with no condition

Switch without a condition is the same as switch true.

This construct can be a clean way to write long if-then-else chains.

package main

import (
	"fmt"
	"time"
)

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

A defer statement defers the execution of a function until the surrounding function returns.

The deferred call’s arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.

package main

import "fmt"

func main() {
	defer fmt.Println("world")

	fmt.Println("hello")
}

Stacking defers

Deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out order.

To learn more about defer statements read this blog post.

package main

import "fmt"

func main() {
	fmt.Println("counting")

	for i := 0; i < 10; i++ {
		defer fmt.Println(i)
	}

	fmt.Println("done")
}

More types: structs, slices, and map

Pointers

Go has pointers. A pointer holds the memory address of a variable.

The type *T is a pointer to a T value. Its zero value is nil.

var p *int

The & operator generates a pointer to its operand.

i := 42
p = &i

The * operator denotes the pointer’s underlying value.

fmt.Println(*p) // read i through the pointer p
*p = 21         // set i through the pointer p

This is known as “dereferencing” or “indirecting”.

Unlike C, Go has no pointer arithmetic.

package main

import "fmt"

func main() {
	var p *int
	i := 10
	p = &i
	*p += 10
	fmt.Println(p, *p, &p)
}

Structs

A struct is a collection of fields.

(And a type declaration does what you’d expect.)

package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

type Foo struct {
	B    int
	A, R int
}

func main() {
	fmt.Println(Vertex{1, 2})
	var foo = Foo{1, 2, 3}
	fmt.Println(foo)
}

Struct Fields

Struct fields are accessed using a dot.

package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
	v := Vertex{1, 2}
	v.X = 4
	fmt.Println(v)
}

Pointers to structs

Struct fields can be accessed through a struct pointer.

The indirection through the pointer is transparent.

package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
	v := Vertex{1, 2}
	p := &v
	p.X = 1e9
	fmt.Println(v)
}

Struct Literals

A struct literal denotes a newly allocated struct value by listing the values of its fields.

You can list just a subset of fields by using the Name: syntax. (And the order of named fields is irrelevant.)

The special prefix & returns a pointer to the struct value.

package main

import "fmt"

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)
	var v4 = Vertex{Y: 10, X: 11}
	fmt.Println(v4, &v4, *(&v4))
}

Arrays

The type [n]T is an array of n values of type T.

The expression

var a [10]int

declares a variable a as an array of ten integers.

An array’s length is part of its type, so arrays cannot be resized. This seems limiting, but don’t worry; Go provides a convenient way of working with arrays.

package main

import "fmt"

func main() {
	var a [2]string
	a[0] = "Hello"
	a[1] = "World"
	fmt.Println(a[0], a[1])
	fmt.Println(a)
	var b [2]int
	b[0] = 1
	b[1] = 2
	fmt.Printf("%T,%v", b, b)
}

Slices

A slice points to an array of values and also includes a length.

[]T is a slice with elements of type T.

package main

import "fmt"

func main() {
	var a = [...]int{1, 2, 3, 4, 5, 6, 7}
	var s1 = a[1:3]
	for i := 0; i < len(s1); i++ {
		fmt.Println(s1[i])
	}
	s1[0] = 10

	fmt.Println(a)
	fmt.Println(s1)
}

Slicing slices

Slices can be re-sliced, creating a new slice value that points to the same array.

The expression

s[lo:hi]

evaluates to a slice of the elements from lo through hi-1, inclusive. Thus

s[lo:lo]

is empty and

s[lo:lo+1]

has one element.

package main

import "fmt"

func main() {
	s := []int{2, 3, 5, 7, 11, 13}
	fmt.Println("s ==", s)
	fmt.Println("s[1:4] ==", s[1:4])

	// missing low index implies 0
	fmt.Println("s[:3] ==", s[:3])

	// missing high index implies len(s)
	fmt.Println("s[4:] ==", s[4:])
}

Making slices

Slices are created with the make function. It works by allocating a zeroed array and returning a slice that refers to that array:

a := make([]int, 5)  // len(a)=5

To specify a capacity, pass a third argument to make:

b := make([]int, 0, 5) // len(b)=0, cap(b)=5

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4
package main

import "fmt"

func main() {
	a := make([]int, 5)
	b := make([]int, 0, 5)
	c := a[:3]
	fmt.Println(c)
	d := b[:2]
	fmt.Println(d)
}

Nil slices

The zero value of a slice is nil.

A nil slice has a length and capacity of 0.

package main

import "fmt"

func main() {
	var z [1]int
	fmt.Println(z, len(z), cap(z))
	//if z == nil {
    	fmt.Println("nil!")
	//}
}

Adding elements to a slice

It is common to append new elements to a slice, and so Go provides a built-in append function. The documentation of the built-in package describes append.

func append(s []T, vs ...T) []T

The first parameter s of append is a slice of type T, and the rest are T values to append to the slice.

The resulting value of append is a slice containing all the elements of the original slice plus the provided values.

If the backing array of s is too small to fit all the given values a bigger array will be allocated. The returned slice will point to the newly allocated array.

package main

import "fmt"

func main() {
	var a []int
	printSlice("a", a)

	// append works on nil slices.
	a = append(a, 0)
	printSlice("a", a)

	// the slice grows as needed.
	a = append(a, 1)
	printSlice("a", a)

	// we can add more than one element at a time.
	a = append(a, 2, 3, 4)
	printSlice("a", a)
}

func printSlice(s string, x []int) {
	fmt.Printf("%s len=%d cap=%d %v\n",
		s, len(x), cap(x), x)
}

Range

The range form of the for loop iterates over a slice or map.

package main

import "fmt"

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

Range continued

You can skip the index or value by assigning to _.

If you only want the index, drop the “, value” entirely.

package main

import "fmt"

func main() {
	pow := make([]float64, 10)
	for i := range pow {
		// invalid operation: 1 << uint(i) (shift of type float64)
		// pow[i] = float64(1<<uint(i)) + 0.1
		j := 1 << uint(i)
		pow[i] = float64(j) + 0.1
	}
	for i := range pow {
		fmt.Printf("%d\n", i)
	}
}

Maps

A map maps keys to values.

Maps must be created with make (not new) before use; the nil map is empty and cannot be assigned to.

package main

import "fmt"

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

Map literals

Map literals are like struct literals, but the keys are required.

package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m = map[string]Vertex{
	"Bell Labs": Vertex{
		40.68433, -74.39967,
	},
	"Google": Vertex{
		37.42202, -122.08408,
	},
}

func main() {
	fmt.Println(m)
	m2 := make(map[int]string)
	for i := 0; i < 10; i++ {
		m2[i] = "Hello"
	}
}

Map literals continued

If the top-level type is just a type name, you can omit it from the elements of the literal.

package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m = map[string]Vertex{
	"Bell Labs": {40.68433, -74.39967},
	"Google":    {37.42202, -122.08408},
}

func main() {
	fmt.Println(m)
}

Mutating Maps

Insert or update an element in map m:

m[key] = elem

Retrieve an element:

elem = m[key]

Delete an element:

delete(m, key)

Test that a key is present with a two-value assignment:

elem, ok = m[key]

If key is in m, ok is true. If not, ok is false and elem is the zero value for the map’s element type.

Similarly, when reading from a map if the key is not present the result is the zero value for the map’s element type.

Note: if elem or ok have not yet been declared you could use a short declaration form:

elem, ok := m[key]
package main

import "fmt"

func main() {
	m := make(map[string]int)

	m["Answer"] = 42
	fmt.Println("The value:", m["Answer"])

	m["Answer"] = 48
	fmt.Println("The value:", m["Answer"])

	delete(m, "Answer")
	fmt.Println("The value:", m["Answer"])

	v, ok := m["Answer"]
	fmt.Println("The value:", v, "Present?", ok)
	m["Question"] = 100
	v, ok := m["Question"]
	fmt.Println("The value:", v, "Present?", ok)
}

Function values

Functions are values too.

package main

import (
	"fmt"
	"math"
)

func main() {
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}

	fmt.Println(hypot(3, 4))
}

Function closures

Go functions may be closures. A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is “bound” to the variables.

For example, the adder function returns a closure. Each closure is bound to its own sum variable.

package main

import "fmt"

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

References