Overview

Appendix C. Arrays, Slices, and Maps

Go provides three core collection types to represent and organize data: arrays, slices, and maps. Arrays are fixed-size, contiguous sequences of elements of the same type; slices are flexible, dynamic windows over an underlying array; and maps associate comparable keys with values for fast lookups. In practice, slices are the default choice for ordered data that may grow or shrink, arrays are useful when size is known and fixed (or for low-level interop and memory layout guarantees), and maps are ideal for associative lookups that are typically constant time.

Arrays embed their length in the type, so passing them by value copies the entire data; to avoid large copies and to allow in-place mutation, pass a pointer to the array. Slices, by contrast, are lightweight descriptors (pointer, length, capacity), so passing them copies only the descriptor while still allowing mutation of the shared underlying array; however, structural changes to a slice (like its length after appending) aren’t seen by the caller unless the function returns the updated slice. Slicing operations produce new slice views (optionally with a capacity limit via the three-index form), append returns a new slice and may allocate a larger array, and make can preallocate capacity to reduce allocations. Because multiple slices can alias the same array, mutating one view affects the others; use copy (or append to a fresh slice) to decouple. Strings are immutable byte sequences; converting between strings and byte slices and using copy helps control ownership and mutations.

Maps must be initialized (with a literal or make) before insertion; assigning to a nil map panics. Use bracket syntax to set and get values, delete to remove entries, len to count elements, and clear to empty the map. Reading a missing key yields the zero value of the element type, so prefer the two-value lookup (value, ok) to distinguish “missing” from “present with zero value.” Iteration order over a map is undefined, and maps behave like references: passing them to functions allows in-place mutation visible to the caller. Preallocating with make(map[K]V, capacity) reduces rehashing and allocations as the map grows, and while typical map operations are constant time, heavy hash collisions can degrade performance.

Figure C.1 A byte array with three consecutive byte elements laid out in memory.
Figure C.2 Passing an array to a function copies the entire array.
Figure C.3 Passing the array's pointer to the function only copies 8 bytes.
Figure C.4 A slice and an underlying array. The slice has a pointer that points to the first element of the array that it sees, plus the length from that pointer onward.
Figure C.5 Each slice here describes a different part of the same array.
Figure C.6 Append allocates an array or reuses it depending on capacity.
Figure C.7 Appending won't allocate a new array if a slice has enough capacity.

FAQ

What’s the difference between arrays and slices in Go?

- Arrays have a fixed length, and the length is part of their type (e.g., [3]byte).

- Slices are dynamic views over underlying arrays; they can grow/shrink and are declared without a length (e.g., []byte).

- Arrays copy on assignment/passing; slices are small descriptors that reference array storage.

Why does an array’s length matter for its type, and what are the implications?

- The type includes length and element type (e.g., [3]byte vs [4]byte are different types).

- You can’t add beyond an array’s length; changing size requires changing the type and recompiling.

- Passing large arrays by value copies the entire array.

How should I pass large arrays to functions efficiently?

- Passing an array by value copies all elements (costly for large arrays).

- Prefer passing a pointer to the array (e.g., *[N]T) to avoid copying and allow in-place mutation.

- On 64-bit systems, a pointer is 8 bytes, so copying the pointer is cheap.

What exactly is a slice header?

- A slice is a descriptor with three fields: pointer (to an element in the underlying array), length (visible elements), and capacity (how far it can grow).

- On 64-bit systems a slice header is 24 bytes.

- Slices don’t store elements; the underlying array does.

What’s the difference between a nil slice and an empty slice?

- Nil slice: var s []T is nil and has length 0.

- Empty slice: []T{} is non-nil but also has length 0.

- Prefer checking emptiness via len(s) == 0, not s == nil (unless you specifically need to know initialization).

When I pass a slice to a function, what can and can’t change?

- The slice is passed by value (the header is copied), so changes to the header (length/capacity) don’t affect the caller unless you return the new slice.

- Mutating elements via the slice affects the original array (shared underlying storage).

- Return the slice if you reslice/append inside the function and want the caller to see those changes.

How does append work and when does it allocate?

- append returns a new slice; it may reuse capacity or allocate a new, larger array if needed.

- Always assign the result: s = append(s, x), otherwise you may keep referencing an old array.

- Capacity typically grows geometrically (often doubling up to a point, then ~25% growth).

When should I use make with slices?

- Use make([]T, length, capacity) to preallocate enough capacity and reduce allocations during appends.

- make([]T, 0, N) gives length 0 and capacity N (ideal for appends).

- make([]T, N) gives length and capacity N (pre-filled with zero values), good when writing by index.

What are slice aliasing effects and how do I avoid them?

- Multiple slices can share the same underlying array; mutating one affects the others.

- Strings are immutable, but converting a string to []byte creates a new array for the bytes; copies of that slice still alias each other.

- To avoid aliasing, copy the data: dst := make([]T, len(src)); copy(dst, src) or dst := append([]T{}, src...).

How do maps work: initialization, lookups, and gotchas?

- A zero-value map is nil; assigning to it panics. Initialize with make(map[K]V) or a literal.

- Absent keys return the zero value of V; use v, ok := m[k] to distinguish absent keys.

- Common ops: set/get with m[k], delete(m, k), size with len(m), clear with clear(m).

- Iteration order is unspecified; don’t rely on it. Printing with fmt shows sorted keys for convenience.

- Maps are reference-like; passing them to functions allows in-place mutation. Keys must be comparable (not slices, maps, or funcs).

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Go by Example ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Go by Example ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Go by Example ebook for free