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