Types in Go: Primitive, Reference, and Composite

Primitive Types

Primitive types (also called basic types) are the simplest, fundamental data types that aren't composed of other types:

  • Numeric types:
    • Integers: int, int8, int16, int32, int64, uint, uint8, etc.
    • Floating-point: float32, float64
    • Complex: complex64, complex128
  • Boolean type: bool
  • String type: string

These types store their values directly in the variable's memory location.

Reference Types

Reference types don't store their data directly - they store a reference (address) to where the actual data is stored:

  • Slices: []T (references an underlying array)
  • Maps: map[K]V (references a hash table structure)
  • Channels: chan T (references a communication queue)
  • Functions: func() (references executable code)
  • Pointers: *T (references another variable's memory address)

When we assign a reference type to another variable or pass it to a function, we're copying the reference, not the underlying data.

Composite Types

Composite types are constructed from other types:

  • Arrays: [N]T (fixed-size collection of elements)
  • Structs: struct{ field1 T1; field2 T2 } (collection of fields)
  • Interfaces: interface{ Method() } (set of method signatures)

📌📌 Note that some types can fit in multiple categories:

  • Arrays are composite but not reference types (they copy their values)
  • Slices are both composite AND reference types (they're composed of other types but use reference semantics)
  • Maps are both composite AND reference types (they're composed of other types but use reference semantics)

Examples

// Primitive types - pass by value
a := 5
b := a  // 'b' gets a copy of the value in 'a'
a = 10  // changing 'a' doesn't affect 'b'
// b is still 5

// Reference types - pass by reference (kind of)
s1 := []int{1, 2, 3}
s2 := s1  // 's2' points to the same underlying data as 's1'
s1[0] = 99  // changing data through 's1' is visible through 's2'
// s2[0] is now 99

// Composite but not reference (arrays)
arr1 := [3]int{1, 2, 3}
arr2 := arr1  // arr2 gets a complete copy
arr1[0] = 99  // changing arr1 doesn't affect arr2
// arr2[0] is still 1

Understanding these distinctions helps when thinking about how memory allocation and initialization work in Go. Read Memory Allocation in Go: make, new and var