Tour of Go - Basics
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
.)
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.
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.
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.
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
Multiple results
A function can return any number of results.
The swap
function returns two strings.
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
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.
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.
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.
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.
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.
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.
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.
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.
Numeric Constants
Numeric constants are high-precision values.
An untyped constant takes the type needed by its context.
Try printing needInt(Big)
too.
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.
For continued
As in C or Java, you can leave the pre and post statements empty.
For is Go’s “while”
At that point you can drop the semicolons: C’s while is spelled for in Go.
Forever
If you omit the loop condition it loops forever, so an infinite loop is compactly expressed.
If
The if
statement looks as it does in C or Java, except that the ( ) are gone and the { } are required.
(Sound familiar?)
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.)
If and else
Variables declared inside an if
short statement are also available inside any of the else
blocks.
Switch
You probably knew what switch
was going to look like.
A case body breaks automatically, unless it ends with a fallthrough
statement.
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.
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.
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.
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.
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.
Structs
A struct
is a collection of fields.
(And a type
declaration does what you’d expect.)
Struct Fields
Struct fields are accessed using a dot.
Pointers to structs
Struct fields can be accessed through a struct pointer.
The indirection through the pointer is transparent.
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.
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.
Slices
A slice points to an array of values and also includes a length.
[]T is a slice with elements of type T.
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.
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
Nil slices
The zero value of a slice is nil
.
A nil slice has a length and capacity of 0.
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.
Range
The range
form of the for
loop iterates over a slice
or map
.
Range continued
You can skip the index or value by assigning to _.
If you only want the index, drop the “, value” entirely.
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.
Map literals
Map literals are like struct
literals, but the keys are required.
Map literals continued
If the top-level type is just a type name, you can omit it from the elements of the literal.
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]
Function values
Functions are values too.
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.
References
- Tour of Go,http://tour.golang.org/