variables, constants, and common types

Variable

  • Initializer can be a general expression computed at run time
  • Without an initial value, the variable is given zero-valued - 0 or nil
var x int = 5
x := 5  // go can infer type, and it is easier to read

Declaration of var or const

Group the relevant variables and comment on them if necessary.
// Error codes returned by failures to parse an expression.
var (
    ErrInternal      = errors.New("regexp: internal error")
    ErrUnmatchedLpar = errors.New("regexp: unmatched '('")
    ErrUnmatchedRpar = errors.New("regexp: unmatched ')'")
    ...
)
In this way, you can also know the relationship between them.
var (
    countLock   sync.Mutex
    inputCount  uint32
    outputCount uint32
    errorCount  uint32
)

Constants

  • Created at compile time, which means constants can only be numbers, characters (runes), strings, or booleans
    • Also, you can not have function call when initializing constants, such as
    • const someConstant = math.Sin(math.Pi/4) // invalid
  • A numeric constant has no type until given one, such as explicit conversion

init

func init() {
	// ...
}
  • Called after all the variables declarations and before main() or real execution
  • Can be used for validation
  • There can be multiple init function in a file, and they will be called in order

Allocation with new

new(T) allocates zeroed storage for a new item of type T and returns its address (*T), a value of type T

Constructors and composite literals

return &File{fd, name, nil, 0}

return &File{fd: fd, name: name} // I prefer this because more descriptive

// new(File) equals to &File{}
Composite literals for arrays, slices, maps
a := [...]string   {Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
s := []string      {Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid argument"}

Array (fixed like in C++)

  • Assigning an array to another would copy all the elements
  • Which means, when passing an array to a function, a function receives a copy of the array
The size of an array is part of its type. The types [10]int and [20]int are distinct.
var a [5] int
a[2] = 1
// [0, 0, 1, 0, 0]

a := [5] int {1, 2, 3, 4, 5}
// [1, 2, 3, 4, 5]

Slice (like list in Python)

  • more idiomatic in Go than array
a := [] int {1, 2, 3, 4, 5}
a = append(a, 6)
// [1, 2, 3, 4, 5, 6]
  • Slice hold references to an underlying array (list list in Python), length, and capacity
  • Use built-in functions len and cap to access slice’s metadata
len(slice)
cap(slice)

Two-dimensional slices

type Transform [3][3]float64 // 2d array
type LinesOfText [][]byte // 2d slice
  • Each slice inside a slice can be variable-length
text := LinesOfText{
	[]byte("123456"),
	[]byte("123"),
	[]byte("123456789"),
}
  • Allocate 2d slice for each row with a determined size
// Allocate the top-level slice.
picture := make([][]uint8, YSize) // One row per unit of y.
// Loop over the rows, allocating the slice for each row.
for i := range picture {
	picture[i] = make([]uint8, XSize)
}
  • Allocate 2d slice for each row based on the remaining data
    • However, the big difference might be just the last row if XSize is fixed
// Allocate the top-level slice, the same as before.
picture := make([][]uint8, YSize) // One row per unit of y.
// Allocate one large slice to hold all the pixels.
pixels := make([]uint8, XSize*YSize) // Has type []uint8 even though picture is [][]uint8.
// Loop over the rows, slicing each row from the front of the remaining pixels slice.
for i := range picture {
	picture[i], pixels = pixels[:XSize], pixels[XSize:]
}

Allocation with make

  • make(T, args) creates slices, maps and channels only
  • returns non-zero value of type T
// allocates slice structure; *p == nil; rarely useful
var p *[]int = new([]int)      

// the slice v now refers to a new array of 100 ints
var v  []int = make([]int, 100)

Map (like dict)

  • Maps hold references
vertices := make(map[string]int)

vertices["one"] = 1
vertices["two"] = 2

fmt.Println(vertices)
// map[one:1 two:2]

fmt.Println(vertices["one"])
// 1

// delete
delete(vertices, "one")

// test a key
element, ok = vertices["one"]

Map Literals in Pretty way

package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m = map[string]Vertex{
	"Bell Labs": {40.68433, -74.39967},
	"Google":    {37.42202, -122.08408},
}

func main() {
	fmt.Println(m)
}
💡
Map and slice can not be initiated as const but var.

Implement set using map

fruits := map[string]bool{
	"Apple": true,
	"Banana": true,
}

if fruits["Apple"] {
	...
}

Pointer (just like C++)

func main() {
	variable := 5
	inc(&variable)
	// variable is 6
}

func inc (a *int) {
	*a++
}

Type Assertion

t := i.(T)  // If i does not hold a T, the statement will trigger a panic.

// or
t, ok := i.(T) // this is better

Reference