Mark's Logs

Reading, thinking, and seeing.

Go Lang Notes

| Comments

TL;DR

0. Resources

I. Interaction with other language

C

  • cgo. Include C-libs or even valid C-code by placing these statements as comments immediately above the import “C” line:
1
2
3
4
//#include <stdio.h>
//#include <stdlib.h> 
import "C"
import "unsafe"
  • C.uint, C.long, C.random():
1
2
3
var i int
C.uint(i)
int(C.random())
  • Strings convertion: C.CString(s), C.GoString(cs):
1
2
cstring := C.CString(gostring)
defer C.free(unsafe.Pointer(cstring))
  • Memory allocations made by C code are not known to Go’s memory manager:
1
defer C.free(unsafe.Pointer(Cvariable))
  • Pseudo #cgo directives:
hdfs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// #cgo linux CFLAGS: -I/opt/jdk/include -I/opt/jdk/include/linux
// #cgo linux LDFLAGS: -Llib -lhdfs -L/opt/jdk/jre/lib/amd64/server -ljvm
// #cgo darwin LDFLAGS: -L/usr/lib/java -lhdfs -framework JavaVM
// #include "hdfs.h"
/*
int getlen(char*** ptr) {
    int i = 0;
    while (ptr[i] != NULL) ++i;
    return i;
}
int getl(char*** ptr) {
    int i = 0;
    while (ptr[0][i] != NULL) ++i;
    return i;
}
char* getstring(char*** ptr, int i, int j) {
    return ptr[i][j];
}
*/
import "C"
  • Pointer arithmetic:
1
2
var p *C.hdfsFileInfo
p = (*C.hdfsFileInfo)(unsafe.Pointer(uintptr(unsafe.Pointer(info)) + uintptr(i)*unsafe.Sizeof(C.hdfsFileInfo{})))

C++

  • SWIG (Simplified Wrapper and Interface Generator) supports for calling C++/C code from Go on Linux.

    • Write the SWIG interface file for the lib to be wrapped
    • SWIG generates the C stub functions
    • called using the cgo machinery
  • SWIG does NOT understand all of C++.

II. Basic constructs and elementary data types

Filenames, Keywords, Identifiers

  • Filenames may not contain spaces or any other special chars.
  • Identifiers: valid if begin with a letter (even Unicode) and followed by 0 or more letters or Unicode digits.
  • _ is blank identifier.
  • anonymous
  • 25 keywords or reserved words: break, case, chan, const, continue, default, defer, else, fallthrough, for, func, go, goto, if, import, interface, map, package, range, return, select, struct, switch, type, var
  • 36 predeclared identifiers: append, bool, byte, cap, close, complex, complex64, complex128, copy, false, float32, float64, imag, int, int8, int16, int32, int64, iota, len, make, new, nil, panic, print, println, real, recover, string, true, uint, uint8, uint16, uint32, uint64, uintptr
  • Used punctuation chars: .,,,;,:,...
  • Used delimiters: (), [], {}
  • Automatic semicolon insertion. However, multiple statements must be seperated by ; on one line.

Basic structure and components

Packages, import and visibility

  • Every go-file belongs to one (and only one) package. Many different .go files can belong to one package.
  • Package must be indicated on the first line. A standalone exec belongs to package main; each go app contains one package main; a package name is written in lowercase letters.
  • Package is compiled as a unit; each dir contains one package by convetion.
  • Every piece of code is compiled only once.
  • Apart from _, Ids of code-objs have to be unique in a package.
  • Visibility Rule:

    • Id start with an uppercase, then the ‘obj’ with this identifier is visible in code outside the package: exported, like public in OO.
    • Id start with a lowercase are not visible outside the package: like private.
  • alias package: import fm "fmt"

Functions

  • func main must have no arguments and no return values results.
  • the first { must be on the same line as the func-declaration.
  • the last } is positioned after the function-code in the column beneath function.
  • Every package should have a package comment, a block comment immediately preceding the package statement.
  • Nearly every top-level type, const, var and func, and certainly every exported name in a program should have a comment: doc comment, starts with the name.

Types

  • Types can be elementary, structured, and interfaces.
  • nil: value for structured type which has no real value; also the default value.
  • There is no type-hierarchy.
  • A function can return more than one variable.
  • GO is a statically typed language.
  • order of execution

    • all packages in package main are imported in the order as indicated, in every package;
    • recursively imports packages, but a certain package is imported only once;
    • for every package (in reverse order) all constants and variables are evaluated, and init() if exists;
    • at last the package main; then main() starts executing.

About naming

  • Names of things in Go should be short, concise, evocative;
  • Names should not contain an indication of the package;
  • Use MixedCaps or mixedCaps rather than underscores;
  • No Get...(); but SetName()

Constants

  • Can only be of type boolean, number, or string.
1
const identifier [type] = value // type is optinal
  • Constants must be evaluated at compile time.
  • Constants can be used for enumerations:
1
2
3
4
5
const (
    Unkonwn = 0
    Female  = 1
    Male    = 2
)
  • So does the value iota:
1
2
3
4
5
const (
    a = iota
    b //= iota
    c //= iota
)
  • A new const block or declaration initializes iota back to 0.

Variables

  • all memory in Go is initialized.
  • if the variable has to be exported, it must start with a capital letter.
  • variable scope: global/package scope; local scope.
  • declaration: var a int, a := 1
  • all vars of elementary types (int, float, bool, string, …) are value types, in stack memory.
  • more complex data which usually needs several words are treated as reference types.
  • pointers are reference types, as well as slices, maps, and channels.
  • vars that are referenced are stored in the heap, where gc works.
  • Printing:

    • fmt.Printf(): %s, %v
    • fmt.Sprintf(): return formatted string
    • fmt.Print(), fmt.Println(): automatic formatting
  • := can only be used inside functions, not in package scope.

  • blank identifier _ can also be used to throw away values: _, b = 5, 7
  • init():

    • every source file can contain only 1 init() function
    • initialization is always single-threaded and package dependency guarantees correct exec order
    • used to verify or repair correctness of the program state before real exec begions

Elementary types and operators

  • must be explicit conversion: Go is strongly typed
  • no operator overloading
  • an expression is by default evaluated from left to right
  • format specifier:

    • %t -> booleans
    • %d, %x, %X -> integers
    • %g -> float; %f -> floating point, %e -> scientific notation; also %n.mg, %n.me, %n.mf
    • %b -> bit-representations
    • %U -> UTF-8 code point: U+hhhh notation
    • %p -> pointer representation
    • %T -> complete type specification
    • %#v -> complete output of the instance with its fields
  • int, uint, uintptr are architecture dependent types; but no float type

  • comlex number: real(c), imag(c)
1
2
var c1 complex64 = 5 + 10i
fmt.Printf("%v", c1)
  • &^ -> force a specified bit to 0
  • no error is generated when an overflow occurs during an arithmetic operation
  • pseudo random numbers: rand.Int(), rand.Intn(n), rand.Float32(), rand.Float64(), rand.Seed(sd)
  • operator precedence:

      ^ !
      * / % << >> & &^
      + - | ^
      == != < <= >= >
      <-
      &&
      ||
    
  • character: var ch byte = '\x41', var ch int = '\u0041', var ch int = '\U00101234'

  • type rune an alias for type int32, for Unicode
  • strings are a sequence of UTF-8 characters, value types and immutable arrays of bytes.

    • interpreted strings: surrounded by "intr string"
    • raw strings surrounded by `raw string`
    • length-delimited and do not terminate by a special char as in C/C++; len(str)
    • default value is empty: ""
    • &str[i] is illegal
    • concatenating strings: +, strings.Join()

strings and strconv package

  • strings.IndexRune(s string, ch int) for non-ASCII character
  • strings.HasPrefix(s, prefix string), strings.HasSuffix(s, suffix string)
  • strings.Contains(s, substr string)
  • strings.Index(s, str string), strings.LastIndex(s, str string)
  • strings.Replace(str, old, new, n), if n = -1 all occurrences are replaced
  • strings.Repeat(s string, count int)
  • strings.ToLower(), strings.ToUpper()
  • strings.TrimSpace(s), strings.Trim(s, "\r\n"), strings.TrimLeft(), strings.TrimRight()
  • strings.Fields(s), strings.Split(s, sep)
  • strings.Join(s1 []string, sep string)
  • strings.NewReader(str) -> r.Read() read a []byte; r.ReadByte() r.ReadRune() read next byte or rune
  • to convert a variable of a certain type T to a string will always succeed: strconv.Itoa(), strconv.FormatFloat()

times and dates

  • package time, datatype time.Time, type Duration, type Location
  • time.Now(), t.Day(), t.Add(), etc.
  • time.After, time.Ticker, time.Sleep(Duration d)
  • time.Unix()

Pointers

  • & address of
  • * type modifier, dereference
  • cannot take the address of a literal or a constant
  • pointer arithmetic is NOT allowed
  • nil pointer dereference is illegal

III. Control structures and Functions

  • if else

    • the else if and else keywords must be on the same line as the closing }
    • idiom: omit the else-clause when the if ends in a break, continue, goto or return statement
    • do not use if/else with a return in both branches
    • if val := xxx; val > yyy { ... }
  • switch case

    • break is implicit
    • keyword fallthrough
    • case val1, val2, val3:
    • when the case-statements ends with a return statement, there also has to be a return statement after the } of the switch.
    • type-switch: [see details in interface section]
  • for (range)

    • for ix, val := range coll {}
    • val here is a copy of the value at that index in the collection, so it can only be used in read purpose;
  • Infinite loops: for {}

  • Condition controlled iteration: for condition {}

  • break/continue

    • a break statement always breaks out of the innermost structure in which it occurs; execution continues after the ending } of that structure;
    • continue skips the remaining part of the loop, but the next iteration.
  • Label/goto

    • discouraged.
    • do not declare any new variables between the goto and the label.
  • select for switching between channels

  • Starting an external command or program: os and os/exec package

IV. Arrays and Slices

Arrays

  • An array is a numbered and fixed-length sequence of data items of the same single-type.
  • Length must be a constant expression, non-negative integer value; it is part of the type of the array, e.g. [2]int.
  • Arrays are mutable.
  • An array is a value type, (not a pointer to the first element like in C/C++), and can be created with new().
  • Array literals:

    1. var arrAge = [5]int{1, 2, 3, 4, 5}
    2. var arrAge = [...]int{1, 2, 3, 4, 5}
    3. var arrKV = [5]string{3: "Andy", 4: "Ken"}
  • Arrays are always 1-dimensional, but may be composed to form multidimensional arrays: [3][5]int;

  • The inner arrays have always the same length; Multidimensional arrays are rectangular, except arrays of slices.

Slices

  • A slice is a reference to a contiguous segment of an array (the underlying array).
  • Slice is a reference type.
  • Slice provide a dynamic window onto the underlying array: slice[a:b], b is not included in the slice.
  • A slice is a variable-length array.
  • len(), cap()
  • A slice in memory is in fact a struct with 3 fields: a pointer to the underlying array, the length of the slice, and the capacity of the slice.
  • Create a slice with make()
  • Slices of slices: the lengths may be vary dynamically, jagged.
  • Inner slices must be allocated individually, with make()
  • func append(s []T, x ...T) []T: allocate a new, sufficiently large slice.
  • func copy(dst, src []T) int
  • make a slice of bytes from string: c := []byte(s)

V. Maps

  • A map is a reference type: var map1 map[keytype]valuetyep
  • The length does not have to be known at declaration, but can grow dynamically.
  • Key type can be any type for which the operations == and != are defined.
  • Value type can be any type, even a func() type:
1
2
3
4
5
6
mf := map[int]func() int {
    1: func() int { return 10 },
    2: func() int { return 20 },
    5: func() int { return 50 },
}
mf[2]()
  • Arrays, slices and structs cannot be used as key type, but pointers and interface type can.
  • One way to use structs as a key is to provide them with a Key() or Hash() method.
  • Map indexing is much faster than a linear search; but still 100x slower than direct array or slice indexing.
  • Create maps by make(), never new().
  • Capacity: make(map[key]value, cap)
  • Test if a key-value exists in a map: _, exists := map1[key1]
  • Remove: delete(map1, key1)
  • for-range to get all keys or values.
  • If the value type of a map is acceptable as a key type, and the map values are unique, inverting can be done easily, via for-range.

VI. Packages

  • import “path or url to the package”
  • By convention, each package (all the go files belonging to it) resides in its own subdirectory, which has the same name as the package.
  • import . "packa": use items from the package ‘packa’ without qualifying with the package name;
  • import _ "packa": imported for side-effects only, the init functions are executed and global variables initialized;
  • import external packages: go get URL, installed at $GOPATH/src/ or $GOROOT/src/, then call go install URL
  • import goex "codesite.ext/author/GoExample/goex"
  • Use go doc for custom packages

VII. Methods

  • Method is a function that acts an variable of a certain type, called receiver.
  • The receiver can be almost anything: even a function type or alias type for int, bool, string or array.
  • The receiver cannot be an interface type.
  • The receiver cannot be a pointer type, but it can be a pointer to any of the allowed types.
  • An alias of a certain type *doesn’t have the methods defined on that type.
  • If the method does not need to use the value recv, discard it by _.
  • A method and the type on which it acts must be defined in the same package. Use an alias type, or struct instead.
  • The receiver must have an explicit name, and this name must be used in the method.
  • The receiver type is called the (receiver) base type; it must be declared within the same package as all of its methods
  • If for a type T a method Meth() exists on *T and t is a variable of type T, then t.Meth() is automatically translated to (&t).Meth().
  • Pointer and value methods can both be called on pointer or non-pointer values.
  • It should not be possible that the fields of an object can be changed by 2 or more different threads at the same time. Use methods of the package sync.
  • When an anonymous type is embedded in a struct, the visible methods of that type are inherited by the outer type:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type Point struct {
    x, y float64
}

func (p *Point) Abs() float64 {
    return math.Sqrt(p.x*p.x + p.y*p.y)
}

type NamedPoint struct {
    Point
    name string
}

func main() {
    n := &NamedPoint{Point{3,4}, "Pythagoras"}
    fmt.Println(n.Abs())
}
  • A method in the embedding type with the same name as a method in an embedded type overrides this.
1
2
3
func (n *NamedPoint) Abs() float64 {
    return n.Point.Abs() * 100
}
  • Embedding multiple anonymous types -> multiple inheritance.
  • Structs embedding structs from the same package have all access to one another’s fields and methods.
  • Embed functionality in a type:

    • Aggregation/composition: include a named field of the type of the wanted functionality
    • Embedding: anonymously embed the type of the wanted functionality
  • String() method:

    • do not make mistake of defining String() in terms of itself -> infinite recursion.
  • Summarzed: in Go types are basically classes. Go does not know inheritance like class oriented OO languages.

  • More OO capabilities: goop provides Go with JavaScript-style objects but supports multiple inheritance and type-dependent dispatch.
  • Suppose special action needs to be taken right before an object obj is removed from memory (gc), like writing to a log-file, achieved by:
1
runtime.SetFinalizer(obj, func(obj *typeObj))
  • SetFinalizer does not execute when the program comes to an normal end or when an error occurs, before the object was chosen by the gc process to be removed.

VIII. Interface

Polymorphism

  • An interface defines abstract method set, but cannot contain variables;
  • Interface internal:

      +------------+------------+
      |            |   method   | 
      |  receiver  |   table    |
      |            |   pointer  |
      +------------+------------+
    
    • Thus, pointers to interface values are illegal.
    • Table of method pointers is built through runtime reflection capability.
  • Multiple types can implement the same interface;

  • A type that implements an interface can also have other methods;
  • A type cam implements many interface;
  • An interface type can contain a reference to an instance of any of the types that implement the interface: dynamic type;
  • Interface embedding interface(s): enumerating the methods;
  • type assertions:

    • v := varI.(T) // unchecked type assertion
    • if v, ok := varI.(T); ok {...} // checked type assertion
    • varI must be an interface variable.
    • testing if a value implements an interface.
  • type switch:

1
2
3
4
5
6
7
8
switch t := var.(type) { // t can be omited
case a:
    //
case b:
    //
default:
    //
}
  • An interface value can also be assigned to another interface value, as long as the underlying value implements the necessary methods.

  • Summarized:

    • Pointer methods can be called with pointers;
    • Value methods can be called with values;
    • Value-receiver methods can be called with pointer values because they can be dereferenced first.
    • Pointer-receiver methods cannot be called with values, because the value stored inside an interface has no address.

Empty Interface

  • The empty interface has no methods: type Any interface{}.
  • Any variable, any type implements it.
  • It can through assignment receive a variable of any type.
  • Each interface{} takes up 2 words in memory: one word for the type, the other word for either value or pointer to value.
  • Can perform templates role in container.
  • copying a data-slice in a slice of interface{} dose not work:
1
2
3
var dataslice []myType = getSlice()
var interfaceSlice []interface{} = dataslice
// must be done explicitly with for-range, e.g.
  • Overloading functions: func DoSomething(f int, a ...interface{}) (n int, errno error)

Dynamic typing

  • In Go, methods and data are treated orthogonally, loosely coupled.
  • duck typing in dynamic languages like Python and Ruby.
  • However, the implementation requirements is statically checked by the compiler;

Object-orientedness of Go: summary

  1. Encapsulation:

    • only 2 access-levels: package scope, and exported;
    • a type can only have methods defined in its own package;
  2. Inheritance: embedding of one or more types;

  3. Polymorphism: through interface.

IX. Reflection

Metaprogramming

1
2
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
  • Kind() function and kind constants;
  • Interface() recovers the (interface) value;
  • Modifying a value through reflection:

    • CanSet() -> settability;
    • v := reflect.ValueOf(x) creates a copy of x;
    • to change value of x, must pass the address of x: v := reflect.ValueOf(&x)
    • to make it settable: v = v.Elem()
  • Reflection on structs:

    • NumField() gives the number of fields in the struct;
    • call its methods with Method(n).Call(nil);
    • only exported fields of a struct are settable;

Reflection with unsafe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var byteSliceType = reflect.TypeOf(([]byte)(nil))

func AsByteSlice(x interface{}) []byte {
    v := reflect.New(reflect.TypeOf(x))
    h := *(*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr()))
    size := int(v.Type().Elem().Size())
    h.Len *= size
    h.Cap *= size
    return unsafe.Unreflect(byteSliceType, unsafe.Pointer(&h)).([]byte)
    // but in Go1 Unreflect are gone?
}

// or, if you prefer:

func AsByteSlice(x interface{}) []byte {
    v := reflect.New(reflect.TypeOf(x))
    h0 := (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr()))
    size := int(v.Type().Elem().Size())
    h := reflect.SliceHeader{h0.Data, h0.Len * size, h0.Cap * size}
    return unsafe.Unreflect(byteSliceType, unsafe.Pointer(&h)).([]byte)
}

Turning C arrays into Go slices

1
func C.GoBytes(cArray unsafe.Pointer, length C.int) []byte

To create a Go slice backed by a C array (without copying the original data), one needs to acquire this length at runtime and use reflect.SliceHeader.

1
2
3
4
5
6
7
8
9
10
11
import "C"
import "unsafe"
//...
        var theCArray *TheCType := C.getTheArray()
        length := C.getTheArrayLength()
        var theGoSlice []TheCType
        sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&theGoSlice)))
        sliceHeader.Cap = length
        sliceHeader.Len = length
        sliceHeader.Data = uintptr(unsafe.Pointer(&theCArray[0]))
        // now theGoSlice is a normal Go slice backed by the C array

It is important to keep in mind that the Go garbage collector will not interact with this data, and that if it is freed from the C side of things, the behavior of any Go code using the slice is nondeterministic.

X. Error-handling and Testing

  • Always assign an error to a variable within a compound if-statement, making for clearer code.
  • Predefined error interface type: type error interface { Error() string }.
  • Package errors; function fmt.Errorf().
  • syscall.Errno; os.EPERM, etc.

defer-panic-and-recover machanism

Run-time exceptions and panic

  • runtime panic: runtime.Error, RuntimeError();
  • panicking: panic(...); all defer statements are guaranteed to execute and then control is given to the function caller, which receivers this call to panic;
  • std library Must functions, e.g. regexp.MustCompile, template.Must, would panic() when encounter an error;

Recover

  • recover is only useful when called inside a deferred function: it retrieves the error value passed through the call of panic; when used in normal execution a call to recover will return nil and have no other effect.
  • panic causes the stack to unwind until a deferred recover() is found or the program terminates.
1
2
3
4
5
6
7
8
9
10
func proect(g func()) {
    defer func() {
        log.Println("done")
        if err := recover(); err != nil {
            log.Printf("runtime panic: %v", err)
        }
    }()
    log.Println("start")
    g()
}
  • custom packages:

    1. always recover from panic in your package: no explicit panic() should be allowed to cross a package boundary;
    2. return errors as error values to the callers of your package.
  • error-handling scheme with closures: (use a slice of empty interface as parameter and return type, to be more general)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func check(err error) { if err != nil { panic(err) } }
func errorHandler(fn fType1) fType1 {
    return func(a type1, b type2) {
        defer func() {
            if e, ok := recover().(error); ok {
                log.Printf("runtime panic: %v", err)
            }
        }()
        fn(a, b)
    }
}

func f1(a type1, b type2) {
    //...
    f, _, err := // call
    check(err)
    t, err1 := // call
    check(err1)
    //...
}

func main() {
    errorHandler(f1)
    errorHandler(f2)
    //...
}

Testing and Benchmarking in Go

  • Test-programs must be within the same package, and the files must have names of the form *_test.go;
  • _test programs are NOT compiled with the normal Go-compilers, and not deployed in production;
  • Must import "testing", with global functions with names starting with TestZzz: func TestAbc(t *testing.T)
  • To signal a failure:

    1. func (t *T) Fail(): marks the test function as having failed, but continues its execution;
    2. func (t *T) FailNow(): marks failed and stops its execution; execution continues with the next text file;
    3. func (t *T) Log(args ...interface{}): formatted log in the error-log;
    4. func (t *T) Fatal(args ...interface{}): 2+3.
  • go test parameters: -v for verbose;

  • Benchmarking-programs must contains functions starting with BenchmarkZzz: func BenchmarkZyx(b *testing.B)

  • Table-driven tests: use an array to collect the test-inputs and the expected results together;

Tuning and profiling

  • go test -x -v -cpuprofile=prof.out -file x_test.go
  • with runtime/pprof:
CPUProfile.go
1
2
3
4
f, err := os.Create(*cpuprofile)
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
f.Close()
MemoryProfile.go
1
2
3
f, err := os.Create(*memprofile)
pprof.WriteHeapProfile(f)
f.Close()

XI. Multithreading

Goroutine

  • GOMAXPROCS is equal to the number of (concurrent) threads, on a machine with more than 1 core, as many threads as there are cores can run in parallel.
  • When the function main() returns, the program exits: it does not wait for other goroutines to complete.
  • The logic of code must be independent of the order in which goroutines are invoked.

goroutines vs. coroutines

  1. goroutines imply parallelism (or can deployed in parallel), coroutines in general do not
  2. goroutines communicate via channels; coroutines communicate via yield and resume operations

Channel: communication & synchronization

  • Only one goroutine has access to a data item at any given time: so data races cannot occur, by design;
  • var identifier chan datatype; reference type;
  • a channel of channels of int: chanOfChans := make(chan chan int)
  • a channel of functions: funcChan := chan func()
  • communication operator: <-
  • The channel send and receive operations are atomic: they always complete without interruption.
  • Don’t use print statements to indicate the order of sending to and receiving from a channel: this could be out of order with what actually happens due to the time lag between the print statement and the actual channel sending and receiving.
  • By default, communication is synchronous and unbuffered: sends do not complete until there is a receiver to accept the value.
  • Buffered channel: ch1 := make(chan string, buf)
  • Sending to a buffered channel will not block unless the buffer is full; reading from a buffered channel will not block unless the buffer is empty.

XII. Network

  • net package.
  • For web applications: text/template, net/http packages.
  • net/rpc.

XIII. Common Go pitfalls and mistakes

  1. Never use the value in a for-range loop to change the value itself;
  2. Never use goto with a preceding label;
  3. Never forget () after a function-name, specifically when calling on a receiver or invoking a lambda function;
  4. Never use new() with maps, always make;
  5. Never forget to use Flush() when terminating buffered writing;
  6. Do not use global variables or shared memory for concurrent programs;
  7. Use println only for debugging purpose;

  8. Initialize a slice of maps the right way;

  9. Always use the comma, ok form for type assertions;
  10. Make and initialize your types with a factory;
  11. Use a pointer as a receiver for a method on a struct only when the method modifies the structure, otherwise use a value;

  12. One should use a bytes.Buffer to accumulate string content, when concatenating strings.

  13. Due to compiler-optimizations and depending on the size of the strings using a Buffer only starts to become more efficient when the number of iterations is > 15;
  14. defer is only executed at the return of a function, not at the end of a loop or some other limited scope;
  15. No need to pass a pointer to a slice to a function;
  16. Never use a pointer to an interface type, this is already a pointer;
  17. Use goroutines and channels only where concurrency is important;
  18. Goroutines will probably not begin executing until after the loop;
  19. For error handling, do not use booleans.

To be continued

Comments