Go
The programming language.
Arrays, Slices and Strings
It took a year to answer these questions to design [[Array]]s, [[Slice]]s in Go:
fixed-size or variable-size?
is the size part of the type?
what do multidimensional arrays look like?
does the empty array have meaning?
The key step was the introduction of slices, which built on fixed-size arrays to give a flexible, extensible data structure
The declaration var buffer [256]byte
declares the variable buffer
, which holds 256 bytes
. The type of buffer
includes its size, [256]byte
. An array with 512 bytes would be of the distinct type [512]byte
.
Arrays have their place—they are a good representation of a transformation matrix for instance—but their most common purpose in Go is to hold storage for a slice.
Slices: The slice header
A slice is a data structure describing a contiguous section of an array stored separately from the slice variable itself. A slice is not an array. A slice describes a piece of an array.
It's important to understand that even though a slice contains a pointer, it is itself a value. Under the covers, it is a struct value holding a pointer and a length. It is not a pointer to a struct.
The Capacity
field records how much space the underlying array actually has; it is the maximum value the Length
can reach
make
function allocates a new array and creates a slice header to describe it, all at once. The make
function takes three arguments: the type of the slice, its initial length, and its capacity, which is the length of the array that make allocates to hold the slice data
[[Context]]
Can use
WithTimeout
to set [[Timeout]] for a job.
[[Channel]]
A send to a nil channel blocks forever
A receive from a nil channel blocks forever
A send to a closed channel panics
A receive from a closed channel returns the zero value immediately
Others
Go does not have reference variables
Maps and channels are not references. If they were this program would print
false
.When you write the statement
m := make(map[int]int)
the compiler replaces it with a call toruntime.makemap
.// makemap implements a Go map creation make(map[k]v, hint) // If the compiler has determined that the map or the first bucket // can be created on the stack, h and/or bucket may be non-nil. // If h != nil, the map can be created directly in h. // If bucket != nil, bucket can be used as the first bucket. func makemap(t maptype, hint int64, hhmap, bucket unsafe.Pointer) *hmap
As you see, the type of the value returned from
runtime.makemap
is a pointer to aruntime.hmap
structure. We cannot see this from normal Go code, but we can confirm that a map value is the same size as a [[uintptr]]–one machine word.If maps are pointers, shouldn't they be *map[key]value?
"In the very early days what we call maps now were written as pointers, so you wrote *map[int]int. We moved away from that when we realized that no one ever wrote
map
without writing*map
." - Ian TaylorMaps, like channels, but unlike slices, are just pointers to
runtime
types
[[Logging]]
Using
log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds)
With
runtime.Caller
Atomic types
Package atomic provides low-level atomic memory primitives useful for implementing synchronization algorithms.
These functions require great care to be used correctly. Except for special, low-level applications, synchronization is better done with channels or the facilities of the sync package. Share memory by communicating; don't communicate by sharing memory.
Go vs other languages
Word size and alignment
This may relevant to [[cache line]]
Variables allocated
Go compilers will allocate variables that are local to a function in that function’s [[stack]] frame. However, if the compiler cannot prove that the variable is not referenced after the function returns, then the compiler must allocate the variable on the garbage-collected [[heap]] to avoid dangling pointer errors. Also, if a local variable is very large, it might make more sense to store it on the heap rather than the stack.
If a variable has its address taken, that variable is a candidate for allocation on the heap. However, a basic escape analysis recognizes some cases when such variables will not live past the return from the function and can reside on the stack.
Collecting stack memory blocks is also much cheaper than collecting heap memory blocks. In fact, stack memory blocks don't need to be collected. The stack of a goroutine could be actually viewed as a single memory block, and it will be collected as a whole when the goroutine exits.
Read more
Last updated