Skip to main content

Go

Introduction

General

Packages & Modules

  • Every Go program is made up of packages
  • Programs start running in function main of package main
  • import
  • When importing a package, you can refer only to its exported (capitalized) names

Functions

  • func ExportedFunction() {}
  • func unexportedFunction() {}
  • A function can take zero or more arguments
    • A parameter’s type is declared after its name: x int, y int
    • When consecutive parameters share a type, you can omit the type declaration from all but the last one: x, y int
  • A function can return any number of results
    • Return types are declared in (): (string, string)
    • Values can be return explicitly: return x, y
    • Or implicitly if the returned variables are named in the return type: if (x, y int), then return will implicitly return x and y

Variables

  • var names types
    • The var statement declares a list of variable names optionally followed by their types and/or initial values: var c, python, java bool
    • var statement can occur at the package or function level
    • A var declaration can optionally include initial values, in which case the explicit types can be omitted since the variables types are inferred from the values on the right hand side: var c, python, java = true, false, "no!"
    • Variables declared with no initial value are assigned their “zero value” (false for booleans, 0 for numbers, "" for strings)
    • A variable’s type can never change (Go is a “strongly typed” language)
  • := short assignment statement
    • An alternative to a var declaration with an implicit type
      • e.g. var i int = 42 can be replaced with i := 42
    • Only available inside functions (since every package-level statement must begin with a keyword)
  • const names types
    • Constants are declared like variables, with a few exceptions:
    • They must be initialized with a value
    • Their value can never change
    • They can be character, string, boolean, or numeric values
    • They cannot be declared using the :=syntax.
  • Naming:
    • PascalCase or camelCase
    • A variable is exported if its name begins with a capital letter (e.g. YouCanImportMe)
    • Any “unexported” names are not accessible from outside the package (e.g. youCannotImportMe)

Types

  • The expression T(v) converts the value v to the type T
    • e.g. bool()
  • Explicit conversion to a compatible type is required when assigning values of one type to a variable with another, or when operating on values of different types (e.g. with +)

Booleans

  • bool

Numbers

  • When you need a non-floating-point number, you should use int unless you have a specific reason to use a sized or unsigned integer type
  • int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr
  • float32, float64
  • complex64, complex128
  • The intuint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems

Strings

  • string
  • Stored as array of bytes, not characters
  • Special (non-ASCII) characters (e.g. emoji) may comprise multiple bytes
  • UTF-8
  • byte = alias for uint8
  • rune = alias for int32 that can represents a Unicode code point
  • Implications for index lookups

Pointers

  • A pointer holds the memory address of a value
  • The type *T is a pointer to a T value; its zero value is nil
  • The & operator generates a pointer to its operand
  • The * operator denotes the pointer’s underlying value; this is known as “dereferencing” or “indirecting”
  • Golang pointers explained, once and for all • JamieDev 📺

Structs

  • struct is a collection of fields
  • Struct fields are accessed using a dot
  • Struct fields can be accessed through a struct pointer
  • To access the field X of a struct when we have the struct pointer p we could write (*p).X. However, that notation is cumbersome, so the language permits us instead to write just p.X, without the explicit dereference

Arrays & Slices

  • The type [n]T is an array of n values of type T - e.g. var a [10]int
  • An array’s length is part of its type, so arrays cannot be resized
  • A slice, on the other hand, is a dynamically-sized, flexible view into the elements of an array
  • In practice, slices are much more common than arrays
  • The type []T is a slice with elements of type T
  • A slice is formed by specifying two indices, a low and high bound, separated by a colon: a[low : high]
  • This selects a half-open range which includes the first element, but excludes the last one
  • e.g. a[1:4] creates a slice which includes elements 1 through 3 of a

Generics

  • Type placeholders that enable functions to apply the same logic to multiple possible input types

Flow Control Statements

  • Conditionals
    • if short statement; condition {} elseif condition {} else condition {}
    • () are not needed around conditions, but {} are required around each block
    • Any variables declared in the optional short statement can be referenced in the following if/else conditions and blocks
  • Loops
    • Go has only one looping construct, the for loop
    • for init; condition; post {}
    • () are not needed, but {} are required
    • The init and post statements can be omitted (resulting in what other languages call a while loop)
    • The condition expression can also be omitted, resulting in an infinite loop
  • Switches
    • switch statement is a shorter way to write a sequence of if - else statements
    • It runs the first case whose value is equal to the condition expression, then automatically skips the rest
    • In effect, the break statement that is needed at the end of each case in other languages is provided automatically in Go
    • A switch without a condition is the same as switch true, which can be a clean way to write long if-then-else chains
  • Defers
    • 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
    • Deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out order
    • Defer, Panic, and Recover • The Go Blog 📖

Interfaces & Methods

  • reader io.Reader
  • writer io.Writer

Concurrency

  • Concurrency vs parallel execution
  • Goroutines
  • WaitGroup
    • Wait
  • Mutexes
    • Lock/Unlock
    • RLock/RUnlock
    • Be careful to include as few lines as possible inside the lock to avoid unnecessary blocking time
  • Confinement
    • Avoiding the bottlenecks caused by locking by instead “confining” the operations that need to be safe to a different subset of the data than is accessed by the other Goroutines
    • Improve Go Concurrency Performance With This Pattern • An example of refactoring from locks to each Goroutine operating on a reference to a specific index in the slice all the Goroutines reference • Kantan Coding 📺
  • Channels
    • Goroutines that pass data around
    • They hold data
    • They are thread safe (avoiding race conditions when reading/writing data to memory)
    • Allow listening for data to be added or removed from a channel and blocking code execution until those events occur
    • Buffer channel = channel that can store multiple values
    • golang context package explained: the package that changed concurrency forever • Kantan Coding 📺
  • Gist of Go: Concurrency • Anton Zhiyanov 📕

Debugging

  • fmt.Printf("%T %v %q", x, y, z)
    • %T = variable’s type
    • %v = non-string variable’s value
    • %q = string variable’s value

Building Web Apps

Comparison to Rust

Inbox