🐹 Go Guide
This guide contains tips how to write Go code.
Go aka. Golang is a compiled programming language with memory management. The main usage is for HTTP and TCP servers. Especially microservices as Docker container running a Go app image can take as little as 4 MB.
The language aims to keep balance between performance and ease of programming. Simply put, it's a mixture C and Python. Static typing and fast compile times.
Go is at it's best in distributed systems and tasks that can be divided to multiple simple operations as the goroutine concurrency model is really easy to use after you use it once or twice.
There aren't great dedicated IDEs for Go, but I'd recommend Visual Studio Code with Go Plugin for newbies.
Architecture
Go comes with a great set of command-line tools. Learn them well.
go build # compile the program including its dependencies
go install # compile and move executable to $GOPATH/bin for command-line usage
go get # download, compile and install a program from github
go test # run tests on the program
go run # compile temporary executable and run it
gofmt # format and simplify code
godoc # generate and view documentation
I recommend also getting golint
. Gofmt reformats Go source code, whereas golint prints out style mistakes.
# Run this after you have set your GOPATH
go get -u github.com/golang/lint/golint
Workspace is the main point of reference for Go. GOPATH
environmental variable points to your workspace root. The best approach is to have a single GOPATH
if your projects allow it.
# Example: GOPATH is /Projects/myproject_gopath
# There are some script files in the project folder to show what
# different commands would do in this setup.
GOPATH/
bin/ # Programs that have the `main` function are compiled here.
myproject.exe
pkg/ # Programs that don't have the `main` function are compiled here.
windows_amd64/github.com/ruksi/mydependency.a
src/ # Project source codes are here, organized by import path
github.com/
ruksi/
myproject/ # You can host this on Github.
.git/
main.go
main_test.go
README.md
mydependency/
.git/
mydependency.go
mydependency_test.go
README.md
# Create a new worspace directory: .../myproject_gopath
mkdir myproject_gopath && cd myproject_gopath
# Set GOPATH to point to this directory.
# Windows: setx GOPATH %~dp0
# Unix: export GOPATH=$(pwd)
# Pull down the code, dependencies and puts them in your GOPATH/src.
go get -v -d github.com/ruksi/myproject
# If called without the -d, will also compile the packages to bin and pkg
# You can also just create ./src/github.com/ruksi/myproject/main.go file
# and start coding.
Add all GOPATH/bin
s into your PATH
. Makes it possible to run binaries installed by go get
.
Go supports all major version control systems. Native package system has integration with Git, Mercurial, Bazaar and Subversion. You can even download packages from services like GitHub.
import "github.com/ruksi/gosagne"
Every Go program is made up of packages. If you are writing an executable code (versus a library), then you need to define a main
package and a main()
function which will be the entry point. All running asynchronous processes are terminated when main
function returns. Library packages don't need to have the main
function.
package main
import "fmt"
func boring() {
fmt.Println("boring!")
}
func main() {
fmt.Println("start")
go boring() // wont print anything as program exits before this is run
fmt.Println("end")
}
All files run init
function automatically. Each source file may have an init
function that is ran after imports, reading of variables and types, but before the main
call is made if the file has the main
function.
package main
import "fmt"
func boring() {
fmt.Println("boring!")
}
var greeting string
var farewell string
func init() {
greeting = "HELLO WORLD!"
farewell = "BYEBYE WORLD!"
}
func main() {
fmt.Println(greeting)
fmt.Println(farewell)
}
Make your code easy to use. If your repository is exclusively a binary or library, make sure consumers can go get
or import
it by its base path. That is, put the package main or primary importable resource in github.com/name/repo
, and use subdirectories for helper packages.
If your code is primarily a binary, but can be used as a library:
github.com/ruksi/foo/
main.go // package main
main_test.go // package main
lib/
foo.go // package foo
foo_test.go // package foo
If your code is primarily a library, but can be used as a binary:
github.com/ruksi/foo/
foo.go // package foo
foo_test.go // package foo
cmd/
foo/
main.go // package main
main_test.go // package main
Prefer go install
over go build
. If you produce a binary, check out getgb. GOOS + GOARCH + go build = cross platform builds.
Dependencies
Using other packages is done using import
. Imports that you don't use can be renamed to _
while developing, but should never be saved to version control.
package main
import (
"fmt"
_ "time"
)
func main() {
fmt.Println("Hello World!") // => Hello World!
}
Go has excellent standard libraries. It has everything you need to write web server or socket server. Read them through once in a while and use them as you need.
Dependency management can be confusing at first. Go doesn't have any kind of version control for imported dependencies e.g. information that you are using version >1.4.75 of ModuleNameX. The easiest way is to use an external Go dependency management library. Or you can manage them by yourself by having one workspace per project. It works fine as long as you do not have two dependencies that depend on different versions of a same library.
Consider default go vendoring for dependency management: Support came in 1.5 (with GO15VENDOREXPERIMENT=1
env) and default in 1.6. You can use a library for locking the dependencies e.g. glide for maximal control.
Libraries should never vendor their dependencies. Only main
package should be allowed to have a vendor
folder, libraries (no main
package) shouldn't have a vendor
folder. Libraries with vendored dependencies are very hard to use.
Make dependencies explicit. Testing hard-coded default and global libraries is hard. Use many small interfaces to model dependencies.
// bad
func foo() {
resp, err := http.Get("http://example.com")
}
// good
type Doer interface {
Do(*http.Request) (*http.Response, error)
}
func foo(d Doer) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
resp, err := d.Do(req)
// ...
}
Code Layout
Always run formatter on your code. Go has a standardized coding style. You may it customize with a few parameters. Keep the same coding style per project.
# Fix all style problems in Go files.
gofmt -w *.go
# Fix all style problems in Go files with some custom rules.
gofmt -w -tabs=false -tabwidth=4 *.go
# Also try to simplify code.
gofmt -w -tabs=false -tabwidth=4 -s *.go
Naming
Good names are consistent, short and accurate. The greater the distance between a name's declaration and its uses, the longer the name should be.
Almost all name identifiers are in camelCase
. Packages, directories and file are in lowercase without any separators.
# exceptions:
# test files end in `_test`
main.go
main_test.go
Entity access level comes from first character's letter case. When a name starts with a lowercase letter, it is private and unaccessible outside the package. When a name starts with an uppercase letter, it is public and accessible outside the package.
func protectedFunction(t string) {}
func PublicFunction(t string) {}
var protectedVariable = 1
var PublicVariable = 1
type protectedType struct {}
type PublicType struct {}
Acronyms should be all capitals. E.g. ServeHTTP
and IDProcessor
.
Keep local variables short.
// bad
index
reader
// good
i
r
Keep parameters short.
// good
func AfterFunc(d Duration, f func()) *Timer
func Unix(sec, nsec int64) Time
func HasPrefix(s, prefix []byte) bool
Return values of exported functions should only be named for documentation.
// good
func Copy(dst Writer, src Reader) (written int64, err error)
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
Receivers should be one or two characters. Receiver names should be consistent across all methods.
// good
func (b *Buffer) Read(p []byte) (n int, err error)
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request)
func (r Rectangle) Size() Point
Use *er naming for single-method interfaces.
type Reader interface {
Read(p []byte) (n int, err error)
}
Package names should lend meaning to the names they export.
// bad
bytes.ByteBuffer
strings.StringReader
// good
bytes.Buffer
strings.Reader
Variables
Declaring variables without defining. You use var
notation to declare variables. Variables are defined as the default or "zero" value of the type, e.g. 0 for integers.
package main
import "fmt"
func main() {
// integer zero value is 0
var myZeroInt int
fmt.Println("myZeroInt:", myZeroInt) // => 0
// string zero value is ''
var myZeroStr string
fmt.Println("myZeroStr:", myZeroStr) // => ''
// boolean zero value is false
var myZeroBool bool
fmt.Println("myZeroBool:", myZeroBool) // => false
}
Make the zero value useful. The most common zero value, nil
, can be a no-op.
func newFoo(..., cfg fooConfig) *foo {
if cfg.Output == nil {
cfg.Output = ioutil.Discard
}
// ...
}
Automatically defining variables is done with :=
. There is var
-notation and comma
-notation. var
-notation may also define the type. You can introduce multiple variables with a single var
. Colon-notation can only be used inside functions.
package main
import "fmt"
// multiple file-wide variables
var (
one = 1
two = 2
three int = 3
)
func main() {
fmt.Println("Sum:", one + two + three) // => 6
// var-notation declaration, then definition
var myNumber
myNumber = 2
fmt.Println("myNumber:", myNumber) // => 2
// var-notation declaration with type definition
var yourNumber int = 1
yourNumber = 2
fmt.Println("yourNumber:", yourNumber) // => 2
// colon-notation aka. short variable declaration
ourNumber := 1
ourNumber = 2
fmt.Println("ourNumber:", ourNumber) // => 2
}
All defined variables must be used. Otherwise the program won't compile. Using _
as the variable name disables this.
package main
import "fmt"
func main() {
//zero := 0 // would crash, unused variable
_ = 0
var one int
_ = one
two := 2
fmt.Println(two)
numbers := []int{10, 20, 30}
for _, number := range numbers {
fmt.Println(number)
}
}
Watch out for unwanted variable shadowing. Can create hard to find bugs.
package main
import "fmt"
func main() {
x := 1
fmt.Println(x) // => 1
{
fmt.Println(x) // => 1
x := 2 // NOTE, A DECLARATION
fmt.Println(x) // => 2
}
fmt.Println(x) // => 1
fmt.Println("")
y := 1
fmt.Println(y) // => 1
{
fmt.Println(y) // => 1
y = 2 // NOTE, AN ASSIGNMENT
fmt.Println(y) // => 2
}
fmt.Println(y) // => 2
}
const
is used to define constant variables. const
statement can appear anywhere var
-statement can.
package main
import "fmt"
// file-wide constants
// note that these should he uppercase if you want to export them
const (
one = 1
two = 2
three = 3
)
func main() {
fmt.Println("Sum:", one + two + three) // => 6
const myDemonicConstant = 666
// myDemonicConstant = 999 // This would fail.
fmt.Println("myDemonicConstant:", myDemonicConstant) // => 2
}
Enumerated values can be done with iota
. iota
is a predefined variable that holds an integer that is incremented every time it is read. The values of iota
is reset when code encounters a const
declaration.
package main
import "fmt"
const ( // iota is reset to 0
freeEnum1 = iota // 0
freeEnum2 = iota // 1
freeEnum3 = iota // 2
)
type EnumType int
const ( // iota is reset to 0
typedEnum1 EnumType = iota // 0
typedEnum2 // 1
typedEnum3 // 2
)
func (et EnumType) String() string {
var str string
if et == typedEnum1 {
str = "Zero"
} else if et == typedEnum2 {
str = "One"
} else if et == typedEnum3 {
str = "Two"
}
return str
}
func main() {
fmt.Println("freeEnum1:", freeEnum1) // => 0
fmt.Println("freeEnum2:", freeEnum2) // => 1
fmt.Println("freeEnum3:", freeEnum3) // => 0
fmt.Println("typedEnum1:", typedEnum1) // => Zero
fmt.Println("typedEnum2:", typedEnum2) // => One
fmt.Println("typedEnum3:", typedEnum3) // => Two
}
Data Types
Snippets related to specific data types:
// Integer range depends on the environment.
// You can expect it to be at least uint8.
var number uint = 7;
// Byte is between 0 - 255, alias for uint8.
var x byte = 255
// Floating point number, 64-bit, a.k.a. float64.
floatNumber := 3.14195
// Variable `foo` is a pointer to a slice of pointers to `FooType` type stuff.
var foo *[]*FooType
// Both are valid.
var greeting string = "hi there"
greetingFollowUp := "how are you"
Go has exceptional variety of basic variable types.
bool true or false
uint8 the set of all unsigned 8-bit integers (0 to 255)
uint16 the set of all unsigned 16-bit integers (0 to 65535)
uint32 the set of all unsigned 32-bit integers (0 to 4294967295)
uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615)
int8 the set of all signed 8-bit integers (-128 to 127)
int16 the set of all signed 16-bit integers (-32768 to 32767)
int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64 the set of all signed 64-bit integers
(-9223372036854775808 to 9223372036854775807)
uint either 32 or 64 bits, depends on the system
int same number of bits as `uint` but signed
float32 the set of all IEEE-754 32-bit floating-point numbers
float64 the set of all IEEE-754 64-bit floating-point numbers
complex64 the set of all complex numbers with float32 real and imaginary parts
complex128 the set of all complex numbers with float64 real and imaginary parts
uintptr an unsigned integer large enough to store the uninterpreted
bits of a pointer value
string alias for []byte, a slice of bytes
byte alias for uint8
rune alias for int32 (represents a Unicode code point)
new
expression to allocate a zeroed value of the requested type. Returns a pointer to the created variable.
package main
import "fmt"
func main() {
x := new(int)
// note that it's a pointer, not the value
// but you can use * to dereference it
fmt.Printf("%d", *x) // => 0
}
Variables have a static and dynamic type. Static type is the type specified at the compile time while dynamic type is the runtime current type. interface{}
type matches with any other type.
// Non-interface type
var myNumber int = 2 // static: int, dynamic: int
// Interface type
var myValue interface{} = 2 // static: interface{}, dynamic: int
myValue = "foo" // static: interface{}, dynamic: string
Type conversion is done with x.(T)
. Use variables as a different type.
package main
import "fmt"
type myInteface interface {
myMethod()
}
func main() {
// `x` has dynamic type int and value of 7
// `i` has type int and value 7
var x interface{} = 123
var i int = x.(int)
fmt.Println(i)
var convertAndPrint = func(toConvert interface{}) {
// Second return value is a boolean that tells whether the conversion
// failed or not. On failure, converted value is zero value of
// the requested type e.g. 0 if int.
var converted, ok = toConvert.(string)
if ok {
fmt.Println("SUCCESS:", converted)
} else {
fmt.Println("FAILURE!")
}
}
// Conversion will be ok.
var yyy interface{} = "stuff!"
convertAndPrint(yyy)
// Conversion will fail.
var myValue myInteface
convertAndPrint(myValue)
// THIS WILL PANIC:
// panic: interface conversion: int is not main.myInteface: missing method myMethod
var numbers interface{} = 123
willBreak := numbers.(myInteface)
fmt.Println(willBreak)
}
Pointers
Arguments passed by value won't mutate the original value. Arguments passed by reference (pointer) will mutate the original value. You can use &
to turn a value into a pointer and *
to turn a pointer into a value.
package main
import "fmt"
// pass by value, won't mutate the original
func zeroByValue(integerValue int) {
integerValue = 0
}
// pass by reference, will mutate the original
func zeroByPointer(integerPointer *int) {
*integerPointer = 0
}
func main() {
i := 1
fmt.Println("initial:", i) // => 1
zeroByValue(i)
fmt.Println("after zeroByValue:", i) // => 1
zeroByPointer(&i)
fmt.Println("after zeroByPointer:", i) // => 0
}
Should I pass by reference or by value? Decision to pass a pointer vs. a value is not about performance, it's about sharing access to the variable. If you want to share the value with a function, then use a pointer. If you want to copy the value, not shared it, use value receivers.
Concurrently called functions requires value receivers. Pointer receivers are not safe for concurrent access. Value receivers are safe for concurrent access. Use channels to provide concurrent access.
Data Structures
Snippets related to data structures:
Arrays are fixed length. Array definition specifies a length and a data type for the elements.
// Note that following two definitions are separate, incompatible types.
[4]int // Array of four integers. Initialized as [0, 0, 0, 0].
[5]int // Array of five integers. Initialized as [0, 0, 0, 0, 0].
var a [4]int
a[0] = 1
i := a[0]
// i == 1
// a[2] == 0, because 0 is the zero value of integer data type
// len(a) == 4
b1 := [2]string{"Penn", "Teller"} // Initializing with values.
b2 := [...]string{"Penn", "Teller"} // Compiler can do the counting for you.
// Type of b2 is [2]string.
Arrays are always one dimensional. You may have arrays inside an array though.
a := [3][2]int{{1,2}, {3,4}, {5,6}}
Slices are array segments. They behave much like arrays. Slices have data type, length and capacity, but note that slice data types do not specify length or capacity.
// You can use `make` function to create slices.
bytes := make([]byte, 5, 5) // make(Type, Length, Capacity)
// bytes == []byte{0, 0, 0, 0, 0}
// But {} initialization shorthand is more common.
emptySlice := []string{}
letters := []string{"a", "b", "c", "d"}
// You can use slicing so a slice shares the same storage as the original.
moreBytes := []byte{'g', 'o', 'l', 'a', 'n', 'g'}
// moreBytes[1:4] == []byte{'o', 'l', 'a'}
// moreBytes[:2] == []byte{'g', 'o'}
// moreBytes[2:] == []byte{'l', 'a', 'n', 'g'}
// moreBytes[:] == moreBytes
// Arrays can be converted to slices.
// They share the same memory.
myArray := [3]string{"Лайка", "Белка", "Стрелка"}
mySlice := myArray[:]
Slices share memory with the origin array. Modifying elements of a slice modifies the elements of the source. Also, if even one slice exists made from an array, the whole array is kept in the memory.
original := []byte{'r', 'o', 'a', 'd'}
partial := original[2:]
// partial == []byte{'a', 'd'}
partial[1] = 'm'
// partial == []byte{'a', 'm'}
// original == []byte{'r', 'o', 'a', 'm'}
// If you want to have separate memory, use copy.
newOrig := make([]byte, len(original))
copy(newOrig, original)
return newOrig
Slices can be used like dynamic arrays. Built-in functions allow slices to be used like dynamic arrays. But you have total control how they behave.
package main
import "fmt"
func main() {
// Appending elements to a slice.
a := []int{0}
a = append(a, 1, 2, 3)
fmt.Println(a) // => [0 1 2 3]
// Appending a slice to another slice.
b := []string{"John"}
c := []string{"George", "Ringo", "Pete"}
b = append(b, c...)
fmt.Println(b) // => [John George Ringo Pete]
// Double capacity of a
fmt.Println(cap(a)) // => 4
tempA := make([]int, len(a), (cap(a)+1)*2)
copy(tempA, a)
a = tempA
fmt.Println(cap(a)) // => 10
}
All multiline literal items require a trailing comma.
package main
func main() {
x := []int{
1,
2,
}
x = x
}
range
iterates over arrays, slices, maps and channels.
package main
import "fmt"
func main() {
var pow = []int{1, 2, 4}
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
// 2**0 = 1
// 2**1 = 2
// 2**2 = 4
}
Maps are unordered groups of keys and values. Also called hash tables or dictionaries.
package main
import "fmt"
func main() {
// You can use `make` to create maps but literal syntax is more common.
numbers := map[string]int{
"one": 1,
"two": 2,
}
numbers["three"] = 3
fmt.Println("numbers :", numbers)
// Check for existence and accessing values
if value, ok := numbers["three"]; !ok {
fmt.Println("Number three not found!")
} else {
fmt.Println("Number three found:", value)
}
// {} initialization shorthand is more common.
states := map[string]string{
"smile": "happy",
"frown": "sad",
}
fmt.Println("states :", states, "with length", len(states))
// You can also specify an empty map.
colors := map[string]string{}
colors["red"] = "#FF0000"
colors["green"] = "#00FF00"
colors["blue"] = "#0000FF"
fmt.Println("colors :", colors, "with length", len(colors))
// Removing keys.
delete(colors, "green")
// Iterating values.
// Note that values are returned in a semi-random order
// because of optimization Go does in the background.
for key, value := range colors {
fmt.Println("color loop:", key, value)
}
}
Getting a single value from a map is faster than from an array. Array must be iterated through, you can just take a value in the middle of the map.
Limiting map capacity makes them a bit more efficient. Otherwise they take a bit more processing power when they are growing. You cannot change the capacity after it has been created.
package main
func main() {
_ = make(map[string]int, 99)
}
You cannot add items to a nil
map. Works with slices, not with maps.
package main
func main() {
var s []int
s = append(s, 1)
// crashes
// var m map[string]int
// m["one"] = 1
var m map[string]int = map[string]int{}
m["one"] = 1
}
structName{}
, struct literals, is used to create structs.
package main
import "fmt"
type Point struct {
X, Y int
}
var (
p = Point{1, 2} // has type Point
q = &Point{1, 2} // has type *Point
r = Point{X: 1} // Y:0 is implicit
s = Point{} // X:0 and Y:0
)
func main() {
fmt.Println(p, q, r, s) // {1 2} &{1 2} {1 0} {0 0}
}
You cannot modify struct
elements directly. This is because map elements are not addressable. Use temporary variable or pointer map.
package main
import "fmt"
type data struct {
name string
}
func main() {
m1 := map[string]data{"x": {"one"}}
// m1["x"].name = "two" // would crash
x := m1["x"]
x.name = "two"
m1["x"] = x
fmt.Println(m1) // => map[x:{two}]
m2 := map[string]*data{"x": {"one"}}
m2["x"].name = "two"
fmt.Println(m2["x"]) // => &{two}
}
interface
s and struct
s can be used as map keys. They must be a comparable though.
package main
import (
"fmt"
"io"
"os"
)
type BuilderCommit struct {
builder string
commit string
}
type BroadcastWriter struct {
writers map[io.WriteCloser]struct{}
}
func (bw *BroadcastWriter) AddWriter(writer io.WriteCloser) {
if bw.writers == nil {
bw.writers = map[io.WriteCloser]struct{}{}
}
bw.writers[writer] = struct{}{}
}
func (bw *BroadcastWriter) Write(p []byte) {
for w := range bw.writers {
w.Write(p)
}
}
func main() {
bc := BuilderCommit{"linux-amd64", "0cd299"}
fmt.Println(bc)
builds := map[BuilderCommit]bool{}
builds[bc] = true
fmt.Println(builds)
bw := BroadcastWriter{}
bw.AddWriter(os.Stdout)
bw.Write([]byte("Hello World!"))
}
Locking allows using data structures concurrently. Maps and other more complex structures can be used concurrently by using m.Lock()
and m.Unlock()
. You should use channels whenever possible though.
package main
import (
"fmt"
"sync"
)
var l sync.Mutex
var a string
func f() {
a = "hello, world"
l.Unlock()
}
func main() {
l.Lock()
go f()
l.Lock() // will wait for unlock before continuing
fmt.Println(a) // hello, world
}
Control Structures
if
-statement. Note that else
must in the middle of braces or it will not compile.
package main
import "fmt"
func main() {
myName := "Ruksi"
if myName == "Tommi" {
fmt.Println("Hello Tommi!")
} else if myName == "Ruksi" {
fmt.Println("Hello Ruksi!")
} else {
fmt.Println("Hello!")
}
}
if
-statement may also contain other statements.
package main
import "fmt"
func main() {
// assignment is made before the conditional
if myNumber := 9; myNumber < 0 {
fmt.Println(myNumber , "is negative")
} else if myNumber < 10 {
fmt.Println(myNumber , "has 1 digit")
} else {
fmt.Println(myNumber , "has multiple digits")
}
}
switch
-statement. Do not indent switch
and you do not have not break out of them.
package main
import "fmt"
func main() {
myNumber := 1
switch myNumber {
case 0:
fmt.Println("ZERO")
case 1:
fmt.Println("ONE")
case 2:
fmt.Println("TWO")
default:
fmt.Println("DUNNO")
}
}
switch
-statement case
may have multiple conditions.
package main
import "fmt"
func main() {
myNumber := 1
switch myNumber {
case 0, 1, 2:
fmt.Println("A FEW")
default:
fmt.Println("DUNNO")
}
}
switch
-statements maybe used without an expression. This syntax is frequently used with channel receivers.
package main
import "fmt"
func main() {
myNumber := 1
switch {
case myNumber < 0:
fmt.Println("NEGATIVE")
case myNumber == 0, myNumber > 0:
fmt.Println("NON-NEGATIVE")
}
}
for
-loop.
package main
import "fmt"
func main() {
for i := 0; i < 3; i++ {
fmt.Println("i:", i)
}
}
for
-loops work as while-loops.
package main
import "fmt"
func main() {
j := 2
for j >= 0 {
fmt.Println("j:", j)
j--
}
}
for
-loops work as infinite loops.
package main
import "fmt"
func main() {
for {
fmt.Println("This could be infinite!")
break;
}
}
for
-loops work as for-each-loops.
package main
import "fmt"
func main() {
numbers := []int{1, 20, 3, 4, 666}
for _, number := range numbers {
fmt.Println(number)
}
}
You can use labeled for
-, switch
- and select
-statements. Labeled breaks and continues are much like goto
and can be used sparingly. Labels are not indented.
package main
import "fmt"
func main() {
BrLoops: // => Foo!Bar!
for x := 0; x < 10; x++ {
fmt.Print("Foo!")
for y := 0; y < 10; y++ {
fmt.Print("Bar!")
break BrLoops
}
}
fmt.Println()
CntLoops: // => Foo!Bar!Foo!Bar!
for x := 0; x < 2; x++ {
fmt.Print("Foo!")
for y := 0; y < 2; y++ {
fmt.Print("Bar!")
continue CntLoops
}
}
}
Avoid goto
-statements.
package main
import "fmt"
func main() {
fmt.Print("Hello ")
goto labelOne
fmt.Print("I will not print!")
labelOne:
fmt.Print("World!")
}
Custom Types
Types behave much like classes. All non-interface types may have methods associated with them. You can only define methods on types within the same package.
// Office type string.
type Office string
// Method of Office type string.
func (o Office) GetName() string {
return o;
}
Type declaration consist of the type name and a type literal.
// type declarations
type Foo struct {
i int
s string
}
type Qux []string
// type literals
struct {
i int
s string
}
[]string
// anonymous struct
var t struct {
i int
s string
}
Create instances with {}
, the literal notation. Lower case types are private, upper case types are public.
type Person struct {
name string
age int
}
bob := Person{"Bob", 20}
annPtr := &Person{name: "Ann", age: 40}
struct
types have fields and methods.
// House type structure.
type House struct {
name string
}
// Method of House type structure.
func (h House) GetName() string {
return h.name;
}
struct
s are comparable if all its fields are comparable. And the fields exist in both struct
s.
package main
import "fmt"
func main() {
var q struct{ s string }
var r struct{ s string }
var c struct {
s string
t int
}
fmt.Println(q == r) // true
q.s = "hello"
fmt.Println(q == r) // false
r.s = "hello"
fmt.Println(q == r) // true
fmt.Println(c)
// fmt.Println(q == c) won't work, non-matching fields
}
Interfaces only specify required methods. Types implement an interface by having the methods described by the interface definitions. You do not need to explicitly state that it implements the interface.
package main
import "fmt"
// Something can be considered a `FooBaz` if it can `Foo` and `Baz`.
type FooBaz interface {
Foo()
Baz() int
}
// Example structure is considered `FooBaz` as it implements `Foo` and `Baz`.
type Example struct {
importantNumber int
}
func (e Example) Foo() {
fmt.Println("I'm an example! Foo.")
}
func (e Example) Baz() int {
fmt.Println("I'm an example! Baz.")
return e.importantNumber
}
func MakeItYell(fooBaz FooBaz) {
fooBaz.Foo()
fmt.Println(fooBaz.Baz())
}
func main() {
var fooBaz = Example{10}
MakeItYell(fooBaz)
}
Don't require broader interface than you need. Function should only accept the minimal interface they need.
Everything implements an empty interface.
// This accepts any parameter.
func DoSomething(v interface{}) {
// ...
}
Function types. Usually used to specify callback function format.
package main
import "fmt"
type operation struct {
name string
fn func(int, int) int // Function that takes 2 integers and gives one.
}
func main() {
// Creating functions that fit the function type.
operations := []operation{
{"add", func(x, y int) int { return x + y }},
{"sub", func(x, y int) int { return x - y }},
{"mul", func(x, y int) int { return x * y }},
{"div", func(x, y int) int { return x / y }},
{"mod", func(x, y int) int { return x % y }},
}
// Pick one of those operations.
selectedOperation := operations[2]
x, y := 5, 12
fmt.Println( selectedOperation.name, x, y )
fmt.Println( selectedOperation.fn(x, y) )
}
Prefer functions over methods. People from OO background overuse methods. Functions should never depend on a state of a structure. Same input will always produce same output. Methods should depend on the state of a structure.
Methods are bound to a specific type.
Functions accept interfaces as input.
Composition works much like inheritance. You can implement inheritance-like behavior with embedding.
package main
import "fmt"
type Person struct {
Name string
}
func (p *Person) GetName() string {
return p.Name
}
// By not giving a name to Person property, we are using embedding.
// You can then call Employee.GetName()
type Employee struct {
Title string
Person
}
func (e *Employee) GetTitle() string {
return e.Title
}
func main() {
joe := Person{"Joe"}
fmt.Println(joe.GetName())
alice := Employee{"Manager", Person{"Alice"}}
fmt.Println(alice.GetName(), "(", alice.GetTitle(), ")")
}
Functions
Functions that are not associated with any type are called top-level functions.
package main
import "fmt"
// accessible outside the package
func PublicTopLevelFunction(text string) string {
fmt.Println(text)
return text
}
// not accessible outside the package
func privateTopLevelFunction(text string) string {
fmt.Println(text)
return text
}
func main() {
PublicTopLevelFunction("hello!")
privateTopLevelFunction("hello!")
indirectTopLevel := privateTopLevelFunction
indirectTopLevel("indirect hi!")
}
Functions can take any number of trailing arguments with ...
Also called variadic functions.
package main
import "fmt"
func sumPrint(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
func main() {
sumPrint(1, 2) // => [1 2] 3
sumPrint(1, 2, 3) // => [1 2 3] 6
nums := []int{1, 2, 3, 4}
sumPrint(nums...) // => [1 2 3 4] 10
}
Functions can return multiple values.
package main
import (
"fmt"
"math/rand"
"errors"
"time"
)
func badWords(text string) (string, error) {
var err error
if (rand.Intn(2) == 1) {
err = errors.New("Bad word found!")
}
return text, err
}
func main() {
rand.Seed( time.Now().UTC().UnixNano() )
filtered, err := badWords("Kinky!")
if err != nil {
fmt.Println("Error: " + err.Error())
return
}
fmt.Println(filtered)
}
Function literals are defined using func
without a name. They can be saved to variables, passed as argument and returned from a function.
package main
import "fmt"
func main() {
var functionLiteral = func(text string) {
fmt.Println(text)
}
functionLiteral("halo!")
indirectLiteral := functionLiteral
indirectLiteral("indirect halo!")
func() {
fmt.Println("wow! immediate function! so javascript!")
}()
}
Function closures. Functions may close over variables, forming a closure.
package main
import "fmt"
func intSequence() func() int {
i := 0
return func() int {
i += 1
return i
}
}
func main() {
nextId := intSequence()
fmt.Println(nextId())
fmt.Println(nextId())
fmt.Println(nextId())
nextType := intSequence()
fmt.Println(nextType())
}
Methods are created by associating the function with a type. Methods can be defined with value or pointer receiver.
package main
import "fmt"
type MyType struct {}
func (mt MyType) MethodWithValueReceiver(text string) {
fmt.Println(text + ", value receiver!")
}
func (mt *MyType) MethodWithPointerReceiver(text string) {
fmt.Println(text + ", pointer receiver!")
}
func main() {
// Usage:
var t1 MyType
t1.MethodWithValueReceiver("normal call")
t1.MethodWithPointerReceiver("normal call")
// Or, staticly:
MyType.MethodWithValueReceiver(t1, "static call")
(*MyType).MethodWithPointerReceiver(&t1, "static call")
// Or, indirectly:
indirectValueReceiver := MyType.MethodWithValueReceiver
indirectValueReceiver(t1, "indirect call")
indirectPointerReceiver := (*MyType).MethodWithPointerReceiver
indirectPointerReceiver(&t1, "indirect call")
}
Prefer pointer receivers to value receivers. Otherwise the structure is passed by value and the original cannot be edited. Passing by value is also more expensive operation because the value must be copied before passing.
package main
import "fmt"
type Person struct {
name string
age int
}
// Passing by value, suboptimal.
func (p Person) ChangeNameByValue(str string) {
p.name = str
}
// Passing by reference, nice work!
func (p* Person) ChangeNameByPointer(str string) {
p.name = str
}
func (p* Person) GetName() string {
return p.name
}
func main() {
var bob = Person{"Bob", 20}
bob.ChangeNameByValue("Jake")
fmt.Println(bob.GetName()) // => Bob
bob.ChangeNameByPointer("Jake")
fmt.Println(bob.GetName()) // => Jake
}
Interfaces are collections of function signatures. Type methods can also be called through interfaces.
package main
import "fmt"
type MyInterface interface {
MethodName(string)
}
type MyType struct {}
func (MyType) MethodName(x string) {
fmt.Println("1: " + x)
}
type MyTypeToo struct {}
func (*MyTypeToo) MethodName(x string) {
fmt.Println("2: " + x)
}
func main() {
var t MyType
t.MethodName("method call")
var tt MyTypeToo
tt.MethodName("method call")
var i MyInterface = t
i.MethodName("call to interface.")
MyInterface.MethodName(t, "static call to interface.")
i = &tt
i.MethodName("call to the same interface.")
MyInterface.MethodName(&tt, "static call to the same interface.")
}
All custom types can have methods. Even function types can have methods.
package main
import "fmt"
type number int
func (n number) IntegerMethodWhat() {
fmt.Println(n)
}
type FunctionType func(text string) (string)
func (FunctionType) MethodName(x string) {
fmt.Println(x)
}
func main() {
var x number = 1337
x.IntegerMethodWhat() // => 1337
var t1 FunctionType = func(text string) (string) {
return text
}
t1.MethodName( t1("INCEPTION!") ) // => INCEPTION!
}
Defer
defer
-statement is commonly used for clean-up. defer
-statement pushes a function call to be executed after the surrounding function returns.
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close() // Always closed on return.
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close() // Always closed on return.
return io.Copy(dst, src)
}
Deferred function arguments are evaluated at the point of defer
-statement.
package main
import "fmt"
func deferEval() {
i := 0
defer fmt.Println(i)
i = 100
fmt.Print("i prints to ")
}
func main() {
deferEval() // => i prints to 0 (not 100)
}
Deferred functions are executed in Last In First Out order.
package main
import "fmt"
func deferOrder() {
for i := 0; i < 4; i++ {
defer fmt.Print(i, " ")
}
}
func main() {
deferOrder() // => 3 2 1 0
}
Deferred functions may modify the surrounding function's named return values.
package main
import "fmt"
func deferChangeReturn() (i int) {
defer func() {
i++
}()
return 1
}
func main() {
fmt.Println( deferChangeReturn() ) // => 2
}
Deferred functions may recover
from panics. Panic will fail the surrounding function, but normal execution will continue outside the function.
package main
import "fmt"
func recoverThis() (response string) {
defer func() {
if msg := recover(); msg != nil {
fmt.Println("Recovered from", msg)
}
response = "recovered!"
}()
panic("A MAJOR PANIC!")
fmt.Println("This will not be printed.")
return "wat"
}
func main() {
// You could recover here also!
value := recoverThis() // => Recovered from A MAJOR PANIC!
fmt.Println(value) // => recovered!
}
Goroutines
Snippets related to goroutines:
Goroutine is like a lighweight thread. Concurrency in Go is done by running normal functions as goroutines
. The go
keyword runs given functions as usual, but the caller does not wait. But note that if main()
routine returns, all execution stops.
package main
import (
"fmt"
"time"
)
func Log(s string) {
fmt.Println(s)
}
func main() {
go Log("This comes between sleep and awake.")
Log("Sleep!")
time.Sleep(2 * time.Second)
Log("Awake!")
}
Goroutines' main way of communication is channels. You can also use locked data structures but that is more troublesome to maintain deadlock-free.
package main
import "fmt"
func main() {
messages := make(chan string) // Create a channel.
go func() {
messages <- "ping" // Sends to channel.
}()
msg := <-messages // Waits to receive form channel.
fmt.Println(msg)
}
Goroutines are really cheap. It is common to have hundreds of thousands goroutines running.
Channels
Snippets related to channels:
- Go - Timers
- Go - Quit Channel
- Go - Yielding Channels
- Go - Channel Chaining
- Go - Channel Timeout
- Go - Take Best Pattern
- Go - State Machine Pattern
Channels are used to communicate between two or more goroutines. They are typed and usually passed as the function parameter.
// Create an unbuffered channel.
numberChannel := make(chan int)
// Send a value to a channel, and because unbuffered, waits receiver to appear.
numberChannel <- 1
// Waits to receive a value from a channel.
<-numberChannel
// Waits to receive a value from a channel and assigns it.
value := <-numberChannel
// Waits to receive a value from a channel.
// `ok` will be false if channel is closed
value, ok := <-numberChannel
// Variable n is going to be 3 after this.
myChannel := make(chan int)
go func() {
myChannel <- 3
}()
n := <-myChannel
// Channels can contain channels.
inceptionChannel := make(chan chan string)
Channel buffered size defines the number of elements that can be sent on the channel at the same time. They will keep their order in the channel. Sending to a buffered channel block only when the buffer is full. By default buffer size is 0 so every send will block.
// Channel with buffer size of 0.
a := make(chan int)
a <- 1 // Blocks execution until other process receives from the channel.
// Channel with buffer size of 2.
b := make(chan int, 2)
b <- 1 // Doesn't block.
b <- 2 // Doesn't block.
b <- 3 // Blocks execution until other process receives from the channel.
You can limit the communication direction in type definition.
func iCanOnlyReadFromThisChannel(input <-chan int) {}
func iCanOnlySendToThisChannel(output chan<- int) {}
range
consumes all data from a channel. Listens until the channel is closed with close
function.
package main
import (
"fmt"
"time"
)
func printAsLongAsOpen(numbers chan int, done chan bool) {
for i := range numbers {
time.Sleep(time.Second) // Emulate that each process takes 1 second.
fmt.Println(i)
}
done <- true
}
func main() {
numbers := make(chan int)
done := make(chan bool)
go printAsLongAsOpen(numbers, done)
go printAsLongAsOpen(numbers, done)
numbers <- 1
numbers <- 2
time.Sleep(5 * time.Second) // Gives the number 3 after 5 seconds.
numbers <- 3
close(numbers) // Closes the channel so goroutine gets out of the for-loop.
<-done // Waits for the goroutine to finish.
}
Only the sender should close a channel, never the receiver.
close(ch)
Keep in mind that main process can still quit range
prematurely.
// bad
package main
import "fmt"
func main() {
ch := make(chan string)
go func() {
for m := range ch {
fmt.Println("processed:", m)
}
}()
ch <- "cmd.1"
ch <- "cmd.2" // won't be processed as main exits right after
}
// good
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan string)
wg := sync.WaitGroup{}
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
for m := range ch {
fmt.Println("processed:", m)
}
}(&wg)
ch <- "cmd.1"
ch <- "cmd.2"
close(ch) // stops the `range` from looping
wg.Wait() // waits for the goroutine to finish
}
for
- and select
-statements are used to listen multiple channels. Go has a control structure called select
which works as a channel multiplexer, allowing interaction with multiple channel operations or channels. Using just for
keeps the code running in infinite loop until broken out of.
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}()
go func() {
for {
msg := <-c2
fmt.Println(msg)
time.Sleep(time.Second * 4)
}
}()
for i := 0; i < 4; i++ {
select {
case c2 <- "HELLO": // Sends HELLO if someone is receiving from channel.
case msg1 := <-c1: // Receives from c1 if someone is sending.
fmt.Println("received", msg1)
case msg2 := <-c2: // Receives from c2 if someone is sending.
fmt.Println("received", msg2)
default:
fmt.Println("no activity")
}
}
}
The default
case in select
is run if no other case is ready.
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
case <-time.After(100 * time.Millisecond):
fmt.Printf("Timed out!")
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}
Documentation
Use godoc
documntation tool and syntax. godoc
parses source code and produces documentation as plain text or HTML.
Documentation should be full sentences and start with the entity name. You document types, variables, constants, functions and packages with a regular comment directly preceding its declaration.
// Fprint formats using the default formats for its operands and writes to w.
// Spaces are added between operands when neither is a string.
// It returns the number of bytes written and any write error encountered.
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
// ...
}
First line of package comment is shown in package list.
// Package sort provides primitives for sorting slices and user-defined
// collections.
package sort
Comments that are not adjacent to a top-level declaration are omitted. Except those that start with BUG(who)
.
// BUG(r): Title uses for word boundaries does not handle Unicode punctuation.
Blank lines can be used to separate paragraphs.
/*
Package gob manages streams of gobs - binary values exchanged between an
Encoder (transmitter) and a Decoder (receiver). A typical use is transporting
arguments and results of remote procedure calls (RPCs) such as those provided by
package "net/rpc".
The implementation compiles a custom codec for each data type in the stream and
is most efficient when a single Encoder is used to transmit a stream of values,
amortizing the cost of compilation.
*/
Pre-formatted text is indented relative to surrounding comment.
/*
can be sent from or received into any of these Go types:
struct { A, B int } // the same
*struct { A, B int } // extra indirection of the struct
*/
URLs are automatically converted to links in HTML output.
Performance
Go uses only one OS thread by default. You can change this with runtime.GOMAXPROCS()
, even though they don't straight up convert of CPUs used.
package main
import (
"fmt"
"runtime"
)
var _ = runtime.GOMAXPROCS(1)
func main() {
fmt.Println(runtime.GOMAXPROCS(-1)) // => 1
fmt.Println(runtime.NumCPU()) // => 1 (on play.golang.org)
runtime.GOMAXPROCS(20)
fmt.Println(runtime.GOMAXPROCS(-1)) // => 20
runtime.GOMAXPROCS(300)
fmt.Println(runtime.GOMAXPROCS(-1)) // => 256
}
Packages
- Use these on save:
gofmt
, goimports. - Pre-commit hook:
go vet
, gometalinter. - go-kit: Collection of tools to build microservices.