Go Quick Start
Familiarize yourself with the major features of Go. Skip if you already know Go. You are not required to use Go for this project, though.
Syntax
Variables
// Declare variable types
var a int // default is 0
var b string // default is ""
// Assign values
a = 123
b = "hi"
// Declare and initialize
var c int = 123
d := "hi" // Type is inferred automatically
// Assign multiple variables
a, c = c, aAn uninitialized value is guaranteed to be a zero value (0, false, "", nil, etc).
Statements
// If-else has no parentheses
if a != c {
// ...
} else {
// ...
}
// Switch does not fall through like C
switch a {
case 123:
// ...
case 456:
// ...
default:
// ...
}// Declare and use a variable in if
if err := f(); err != nil {
// ...
}// Loops
for i := 0; i < N; i++ {
// ...
}
for zzz {
// ...
}
for {
if xxx {
break
}
if yyy {
continue
}
}Functions
func f(a int, b string) error {
return nil
}
// Multiple return values
func g() (string, bool) {
return "hi", false
}
// Assign multiple return values
s, b := g()
// Named return values
func h() (s string, b bool) {
s = "hi"
b = false
return
}// If a function returns an error, put error last
func foo() (string, error) {
// ...
}
result, err := foo()
if err != nil {
// handle error
} else {
// use result
}Data Types
Numeric Types
// Fixed size
bool, byte
int8, int16, int32, int64
uint8, uint16, uint32, uint64
float32, float64
// Size depends on system: 32-bit or 64-bit
int, uint
// Type conversion
a := 1 // int
var b int64
b = a // compile error
b = int64(a) // convert- Integer overflow and conversion follow two’s complement, no UB like C.
- Type conversion must be explicit.
Pointers/References
a := 1
var ptr *int // declare pointer
ptr = &a // take address
b := *ptr // dereferenceGo has GC, so pointer use is restricted, which includes arbitrary pointer arithmetic or storing pointers as other types. You won’t hit these issues if you avoid unsafe.
struct, method
type C struct {
foo string,
bar int,
}
// Initialize struct
c := C{foo: "hi", bar: 123}
// Access fields
c.bar = 456
bar1 := c.bar
// Pointer
pc := &c
bar2 := (*pc).bar // dereference before using field
bar3 := pc.bar // no need explicit dereference// Normal function
func foo1(c *C) {
c.foo = "hi"
}
// Method with pointer receiver
func (c *C) foo2() {
c.foo = "hi"
}
// Method with value receiver
func (c C) bar() {
println(c.bar)
}
// Call function
foo1(&c)
// Call methods
c.foo2() // pointer
c.bar() // valueFixed-Size Arrays
var a [2]byte // 2 elements, init to 0
var b [2][4]byte // 2 elements, each is [4]byte
// Assign
b = [2][4]byte{{0, 0, 0, 0}, {0, 0, 0, 0}}
b[1][3] = 123Strings
A string is just a some bytes without text encoding. It can convert to/from []byte.
type string struct {
ptr *byte // data
len int // length
}s := "asdf" // {ptr: "asdf", len: 4}
len(s) // 4
// Strings are immutable
s[0] = 'b' // compile error
// Slice without copying
s[2:4] // {ptr: "df", len: 2}
// Concatenation creates a new string
s = "hi" + sslice
In Go, slice actually has 2 unrelated uses shared by the same representation:
- A view of an array, with a pointer and length.
- A growable dynamic array, like C++ vector or Python list.
type sliceT struct {
ptr *T
len int
cap int
}len()andcap()return length and capacity.- A
nilslice is{ptr: nil, len: 0, cap: 0}.
These 2 uses are often confused, so they are explained separately.
a. Slice as an array view
// An array
var arr [8]byte = [8]byte{0, 1, 2, 3, 4, 5, 6, 7}
// Slice an array
s1 := arr[1:5] // {ptr: &arr[1], len: 4, cap: 7}
// Iterate
for i := 0; i < len(s1); i++ {
println(s1[i]) // 1 2 3 4
}
// Use `range` instead
for i := range s1 {
println(s1[i])
}
for i, v := range s1 {
println(v)
}
// Out of bounds causes panic
v := s1[4]
// Reslice, returns a new slice
s2 := s1[2:3] // {ptr: &arr[3], len: 1, cap: 5}
for _, v := range s2 {
println(v) // 3
}Bounds checks:
s[i]:i < lens[i:j]:j <= cap
Usually, reslicing shrinks the len, although expansion is also allowed. When a slice is an array view, only len matters, cap can be ignored.
// shorthands
s[:] // s[0:len(s)]
s[:j] // s[0:j]
s[i:] // s[i:len(s)]b. Slice as a dynamic array
// Allocate array of len 2
arr1 := make([]byte, 2) // {ptr: 0x100, len: 2, cap: 2}
// Allocate array with cap 2, len 0
arr2 := make([]byte, 0, 2) // {ptr: 0x200, len: 0, cap: 2}
// Append elements
arr2 = append(arr2, 123) // {ptr: 0x200, len: 1, cap: 2}
arr2 = append(arr2, 123) // {ptr: 0x200, len: 2, cap: 2}
// Reallocate when cap is exceeded
arr2 = append(arr2, 123) // {ptr: 0x300, len: 3, cap: 8}
// Append multiple elements
arr2 = append(arr2, 2, 3) // {ptr: 0x300, len: 5, cap: 8}Only dynamic slices can be appended. A slice that is an array view must not be appended, since it would modify the array.
Common slice operations
copy(dst, src) // copy data
s1 = slices.Clone(s) // allocate and copy
s = slices.Concat(s1, s2, s2) // concatenate
s = slices.Delete(s, i, j) // delete s[i:j]
s = slices.Insert(s, i, e1, e2) // insert at i
idx = slices.Index(s, v) // find index
slices.Sort(s) // sort in placeA slice as an array view is only a reference and must not be appended. A slice as a dynamic array owns the array and can do any operation. A dynamic slice can be used as an array view, but not the reverse.
interface
An interface is a set of method rules. Any type that implements these methods can be used as the interface.
type Reader interface {
Read(p []byte) (n int, err error)
}
// Type that implements the interface
type T struct { /* ... */ }
func (s *T) Read(p []byte) (n int, err error) { /* ... */ }
// A function that takes an interface as an argument
func useReader(r Reader) { /* ... */ }
obj := &T{} // concrete type
useReader(obj) // converted to interfaceCalling methods via an interface does not depend on the concrete type, similar to abstract classes in OOP. An interface has 2 pointers:
type interfaceFoo struct {
object uintptr // data pointer
typeInfo uintptr // type info
}Converting a value to an interface stores the value pointer and its type info. Method calls use the type info to find the implementation.
An uninitialized interface has both pointers set to nil. This is different from converting a nil pointer value to an interface.
Recover the original type
- Use
iface.(T)to test and extract typeT. interface{}has 0 methods. Any type can convert to it.
var anything interface{}
anything = 1
anything = "asdf"
// Convert back to concrete type
integer, ok := anything.(int) // ok = false
str, ok := anything.(string) // ok = true
// Type switch
switch origin := anything.(type) {
case int:
integer = origin
case string:
str = origin
}Tests
A test file ends with _test.go, e.g., foo_test.go. Test functions starts with Test and takes a *testing.T argument.
package foo
import "testing"
func TestSomething(t *testing.T) {
if 1 + 2 != 3 {
t.Fail()
}
}Run all test cases with this command: go test .