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 typeT
and returns its address (*T
), a value of typeT
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
andcap
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