Allocation with new

Go has two allocation primitives, the built-in functions new and make.

new(T): is a built-in function that allocates memory, but it does NOT initialize the memory, it only zeros it.
That is, new(T) allocates zeroed storage for a new item of type T and returns its address, a value of type *T.
In Go terminology, it returns a pointer to a newly allocated zero value of type T.

The zero-value-is-useful property works transitively.

  1. type SyncedBuffer struct {
  2. lock sync.Mutex
  3. buffer bytes.Buffer
  4. }
  5. // Values of type SyncedBuffer are also ready to use
  6. // immediately upon allocation or just declaration.
  7. p := new(SyncedBuffer) // type *SyncedBuffer
  8. var v SyncedBuffer // type SyncedBuffer

Constructors and composite literals

composite literal: is an expression that creates a new instance each time it is evaluated.

  1. func NewFile(fd int, name string) *File {
  2. if fd < 0 {
  3. return nil
  4. }
  5. f := File{fd, name, nil, 0}
  6. return &f
  7. // combine the last two lines
  8. //return &File{fd, name, nil, 0}
  9. //labele the elements explicitly
  10. //return &File{fd: fd, name: name}
  11. }

The expressions new(File) and &File{} are equivalent.

create for arrays, slices, and maps

  1. array := [...]string{0: "no error", 1: "Eio", 2: "invalid argument"}
  2. slice := []string{10: "no error", 11: "Eio", 12: "invalid argument"}
  3. mapping := map[int]string{20: "no error", 21: "Eio", 22: "invalid argument"}

Allocation with make

The built-in function make(T, args) creates slices, maps, and channels ONLY, and it returns an initialized (not zeroed) value of type T (not *T).

v := make([]int, 100)


Arrays

  • Arrays are values. Assigning one array to another copies all the elements.
  • In particular, if you pass an array to a function, it will receive a copy of the array, not a pointer to it.
  • The size of an array is part of its type. The types [10]int and [20]int are distinct.



Slices

Slices wrap arrays to give a more general, powerful, and convenient interface to sequences of data.

Its run-time data structure holds the pointer, length, and capacity.

Slices hold references to an underlying array, and if you assign one slice to another, both refer to the same array.

slice (here used as a verb) the buffer: slice = slice[0:size]

  1. // signature
  2. func (f *File) Read(buf []byte) (n int, err error)
  3. // buf[0:32]
  4. n, err := f.Read(buf[0:32])

cap
len
append


Two-dimensional slices

define an array-of-arrays or slice-of-slices

  1. type Transform [3][3]float64 // A 3x3 array, really an array of arrays.
  2. type LinesOfText [][]byte // A slice of byte slices.

two ways to allocate a 2D slice

  • allocate each slice independently

    1. // Allocate the top-level slice.
    2. picture := make([][]uint8, YSize) // One row per unit of y.
    3. // Loop over the rows, allocating the slice for each row.
    4. for i := range picture {
    5. picture[i] = make([]uint8, XSize)
    6. }
  • allocate a single array and point the individual slices into it

    1. // Allocate the top-level slice, the same as before.
    2. picture := make([][]uint8, YSize) // One row per unit of y.
    3. // Allocate one large slice to hold all the pixels.
    4. pixels := make([]uint8, XSize*YSize) // Has type []uint8 even though picture is [][]uint8.
    5. // Loop over the rows, slicing each row from the front of the remaining pixels slice.
    6. for i := range picture {
    7. picture[i], pixels = pixels[:XSize], pixels[XSize:]
    8. }

Maps

Maps are built-in data structure that associate values of one type (the key) with values of another type (the element or value).

composite literal syntax

  1. var timeZone = map[string]int{
  2. "UTC": 0 * 60 * 60,
  3. "EST": -5 * 60 * 60,
  4. "CST": -6 * 60 * 60,
  5. "MST": -7 * 60 * 60,
  6. "PST": -8 * 60 * 60,
  7. }
  8. offset := timeZone["EST"]
  9. // distinguish a missing entry from a zero value, the "comma ok" idiom
  10. seconds, ok = timeZone[tz]
  11. // blank identifier
  12. _, present := timeZone[tz]
  13. // use the "comma ok" idiom
  14. func offset(tz string) int {
  15. if seconds, ok := timeZone[tz]; ok {
  16. return seconds
  17. }
  18. log.Println("unknown time zone:", tz)
  19. return 0
  20. }


A set can be implemented as a map with value type bool.

  1. attended := map[string]bool{
  2. "Ann": true,
  3. "Joe": true,
  4. ...
  5. }
  6. if attended[person] { // will be false if person is not in the map
  7. fmt.Println(person, "was at the meeting")
  8. }

to delete a map entry, use the delete built-in function
delete(timeZone, "PDT")


Printing

formatted printing, in the fmt package
Printf, Print, Println
Fprintf, Fprint, Fprintln
Sprintf, Sprint, Sprintln

os.Stdout
os.Stderr

catchall format %v (for “value”), the result is exactly what Print and Println would produce

printing a struct:
%+v: with field names
%#v: full Go syntax

quoted string format: %q, ``%#q (backquotes)
hexadecimal string: %x, ``% x

the type of a value: %T


Append

append built-in function

func append(slice []Type, elems ...Type) []Type

You can’t actually write a function in Go where the type Type is determined by the caller.
That’s why append is built in: it needs support from the compiler.

simple example

  1. x := []int{1,2,3}
  2. x = append(x, 4, 5, 6)

append a slice to a slice

  1. x := []int{1,2,3}
  2. y := []int{4,5,6}
  3. x = append(x, y...)