Slices are the most common composite type in Go. A slice is a flexible, dynamically sized view into an underlying array, and its declaration []int{1, 2, 3} creates one with both length and capacity set. The built-in append adds elements, growing the slice when needed by allocating a new backing array; the runtime typically doubles small slices and grows larger ones by roughly 25% to amortize the cost of repeated reallocation. Two descriptors govern a slice: len(s) reports the number of accessible elements, and cap(s) reports how many elements fit before the next growth. There is a subtle but important distinction between a nil slice (var s []int) and an empty slice (s := []int{} or make([]int, 0)): most operations behave identically, but json.Marshal renders the first as null and the second as []. Assignment between slices shares the underlying array, so changes are visible across both names, while copy(dst, src) duplicates elements into independent storage up to min(len(dst), len(src)). Two-dimensional data is built by composing slices of slices, since Go does not have dynamic multidimensional arrays.
Maps are unordered key-value collections created with make(map[K]V) or with composite literals. Reading a missing key returns the zero value of the value type rather than panicking, but the comma-ok idiom v, ok := m["key"] distinguishes a present-but-zero entry from a missing one. The built-in delete(m, "key") removes an entry (or does nothing if the key is absent), and it is safe to call during iteration. Map elements are not addressable, so &m["key"] is a compile-time error and updates must be done with a plain assignment. Iteration order is intentionally randomized to discourage reliance on it; if deterministic output is required, sort the keys first. Maps are not safe for concurrent use, so access from multiple goroutines requires a mutex or a channel-based protocol.
Structs group related fields under a named type, and Go encourages composition over inheritance through struct embedding. Embedding a type anonymously promotes its fields and methods to the outer struct, so Dog{Animal{Name: "Rex"}, "Labrador"} lets callers write d.Name directly. This is a powerful alternative to classical inheritance and keeps the type hierarchy flat. Arrays, by contrast, have a fixed size baked into their type ([3]int versus [4]int are different types), so slices are overwhelmingly more common in idiomatic Go; a slice can be obtained from an array with the slice expression arr[1:3]. Sorting is handled by the sort package and, since Go 1.21, the slices package: sort.Ints covers built-in numeric types, sort.Slice accepts a custom less function (but is not stable), and sort.SliceStable preserves the relative order of equal elements. The strings package complements these collections with utilities like strings.Builder for efficient concatenation, strings.Repeat for repeated copies, and strings.SplitN for splitting into at most N substrings.