By convention, the package name is the same as the last element of the import path
A name is exported (accessible outside a package) if it begins with a capital letter
Types & Variables
// Defntype MyInt int// Simple typesx intp *inta [5]int// Variablesvar indexed = a[5]var deref = *p// Declarationsvar a, b, c intfunc a() { var d, e, f string}// Initializersvar i, j int = 1, 2 // Can omit type, it is inferred// Short hand declarationsfunc a() { a := 2}// Block declarationsvar ( a int = 2 b string = "hi")// Type conversioni := 42f := float64(i)// Constantsconst Word = "hello"
Types
bool
string
int, int8-64, uint8-64, uintptr
byte (alias for uint8)
rune (alias for int32 - unicode codepoint)
float32-64
complex64-128
Variables without initialization have zero values
0 for numerics
false for boolean
"" for strings
nil for pointers, slices, maps
Type conversions are explicit for assignments
Constants cannot be declared using :=
Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type. This cannot be used outside functions.
Functions
// Defnfunc add(x int, y int) int { return x + y}func add(x, y int) {} // Sugar to omit first types when types are same// Multiple resultsfunc swap() (string, string) { return "", ""}a, b = swap()// Named/Naked returnsfunc split() (x, y int) { // Named return used to document meaning x = 2 y = 3 return // Implied that x and y are returned}// Mainfunc main() { add(2, 3)}// Function valuesfunc compute(fn func(int, int) int) int { return fn(2, 3)}// Closuresfunc adder() func(int) int { return func(x int) int {}}
Functions are values and can be passed around
Conditionals and Iteration
// forfor i := 0; i < 10; i++ {}for ; i < 10; {} // Optional init, postfor i < 10 {} // Similar to a while loopfor {} // Infinite loopfor index, elem := range slice {}for key, value := range maps {}// ifif x < 0 {} else {}if x := 10; x < 20 {} // Can start with a statement, scoped to if/else// switchswitch a := "hi", a { case "ho": fmt.Println("h") default: fmt.Println("d")}switch { // No condition, switch true (if else chain) case a < 12: case a < 20: default:}// deferfunc main() { defer fmt.Println("world") fmt.Println("hello")}
Go has only one loop construct - for
The basic for loop has three components separated by semicolons
the init statement: executed before the first iteration
the condition expression: evaluated before every iteration
the post statement: executed at the end of every iteration
Go switch does not require break, its provided automatically
A defer statement defers the execution of a function until the surrounding function returns.
Deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in LIFO.
Usually used in cleanup functions that exist right next to allocation/initialization functions.
// Pointersvar p *int // Declarationi := 42p = &i // Ref/Creationj := *p // Deref/Indirection// Structtype Point struct { X int Y int}p := Point{1, 2} // Initializationp := Point{X: 1} // Named init, Y is zero valuedp.X = 3 // Accessptr := &pptr.X = 3 // Implicit deref and access// Arrayvar a [10]intarr := [6]int{1, 2, 3, 4, 5, 6}arr[0]// Sliceprimes := [6]int{2, 3, 5, 7, 11, 13}var s []int = primes[1:4] // [) rangevar s []int = primes[:]// Mapsvar m map[string]Pointm = make(map[string]Point) // Initialisationm["A"] = Point{X: 1, Y: 2}var m = map[string]Point{"A": Point{1, 2}, "B": Point{2, 5}}var m = map[string]Point{"A": {1, 2}, "B": {2, 5}}elem, ok := m["C"] // ok tests if key is present
Go has no pointer arithmetic
Go allows implicit dereference and access of fields of struct pointers
Arrays cannot be resized and the length is part of its type
Slices
A slice is a dynamically sized, flexible view into elements of an array, it indicates no amount in its type - []T
A slice does not store any data, its a view/ref
Changing the elements of a slice modifies the array
Other slices also reflect those changes
Maps
A nil map has no keys, nor can keys be added. The make function initializes it
Methods and interfaces
// Methodfunc (p Point) Sum() int { return p.X + p.Y}p := Point{1, 2}p.Sum()ptr := &Point{1, 2}ptr.Sum() // Auto deref// Interfacetype Abser interface { Abs() int}func (p *Point) Abs() int { return p.X + p.Y}var a Abserp := Point{1, 2}a = &pa = p // Does not work as Point does not implement Abser, only *Point does
Methods
Go does not have classes, instead you can define methods (a function with a receiver arg) on types
Can be declared on non-struct types
The receiver arg can be a pointer too
You cannot declare a method with a receiver whose type is defined in another package
Go has automatic ref/deref creation when calling methods with a pointer receiver on values and calling methods on pointer values respectively
All methods on a given type should have either value or pointer receivers, but not a mixture of both
Interface
Set of method signatures
A value of interface type can hold any value that implements those methods
Interface need not be explicitly declared on methods - no implements keyword
Under the hood, interface values can be thought of a tuple - (value, type)
If the concrete value inside the interface itself is nil, the method will be called with a nil receiver. The nil value can be handled inside the method (receiver should be a pointer)