Chapter 14: Basic Data Types
Basic Data Types — the building blocks that every variable (or constant) in Go must have.
Basic data types (also called primitive types or predeclared types) in Go are the simplest, built-in kinds of values the language understands directly — no need to define them yourself.
Go keeps this list small and clean on purpose (only ~25 predeclared types total, and only ~15 are “basic” ones most people use daily). The official place to see them is:
- A Tour of Go → slide “Basic types”
- Go Spec → section “Types”
They fall into four main groups:
- Boolean (true/false)
- Numeric (integers, floating-point, complex)
- String (text)
- Special aliases (byte, rune — very common)
I’ll explain each group like we’re sitting together with VS Code open: what it stores, size/memory, zero value, when to use it, signed vs unsigned, common pitfalls, and lots of copy-paste examples.
1. Boolean Type — bool
- Only two possible values: true or false
- Zero value: false
- Size: usually 1 byte (implementation detail)
- Used for: conditions, flags, switches, success/failure
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package main import "fmt" func main() { var isStudent bool = true // explicit hasCoffee := false // short declaration fmt.Printf("Is student? %t\n", isStudent) // true fmt.Printf("Has coffee? %t (zero value? %t)\n", hasCoffee, hasCoffee == false) } |
Output:
|
0 1 2 3 4 5 6 7 |
Is student? true Has coffee? false (zero value? true) |
Tip: Almost always use short declaration := — very rare to write var … bool = true
2. Numeric Types — The Big Family
Go gives you precise control over size → choose exactly how many bits you need (very useful for performance, serialization, protocols).
a) Signed Integers (int, int8, int16, int32, int64)
Can be negative or positive.
| Type | Bits | Range (approx) | Zero value | Common use |
|---|---|---|---|---|
| int | 32 or 64 (depends on system — 64-bit now usual) | -2³¹ to 2³¹-1 or -2⁶³ to 2⁶³-1 | 0 | General purpose numbers (most common) |
| int8 | 8 | -128 to 127 | 0 | Small counters, bytes with sign |
| int16 | 16 | -32,768 to 32,767 | 0 | Older systems, some protocols |
| int32 | 32 | -2,147,483,648 to 2,147,483,647 | 0 | Unicode code points (rune alias) |
| int64 | 64 | huge | 0 | Timestamps, large counters, IDs |
b) Unsigned Integers (uint, uint8, uint16, uint32, uint64)
Only non-negative (including 0).
| Type | Bits | Range | Zero value | Common use |
|---|---|---|---|---|
| uint | 32 or 64 | 0 to 2³²-1 or 0 to 2⁶⁴-1 | 0 | Sizes, indices, bit masks |
| uint8 | 8 | 0 to 255 | 0 | byte alias — most common for raw bytes |
| uint16 | 16 | 0 to 65,535 | 0 | Colors (RGB565), ports |
| uint32 | 32 | 0 to 4,294,967,295 | 0 | IPv4 addresses, hashes |
| uint64 | 64 | enormous | 0 | File sizes, crypto nonces |
c) Floating-Point (float32, float64)
Decimal numbers.
| Type | Precision | Approx range | Zero value | Recommendation |
|---|---|---|---|---|
| float32 | ~6–7 digits | ±1.18×10⁻³⁸ to ±3.4×10³⁸ | 0.0 | Rarely — use only when memory critical |
| float64 | ~15 digits | ±2.23×10⁻³⁰⁸ to ±1.80×10³⁰⁸ | 0.0 | Default for most floating-point work |
float64 is the default — almost always use float64 unless you have a very good reason.
d) Complex Numbers (complex64, complex128)
Rare for most people, but built-in.
- complex64 = float32 real + float32 imaginary
- complex128 = float64 real + float64 imaginary (most common)
|
0 1 2 3 4 5 6 7 |
c := 3 + 4i // inferred complex128 fmt.Printf("%v (type %T)\n", c, c) // (3+4i) (complex128) |
3. String Type — string
- Immutable sequence of bytes (usually UTF-8 encoded text)
- Zero value: “” (empty string)
- Very efficient — len() gives byte count, not rune count
- Concat with +, compare with ==
|
0 1 2 3 4 5 6 7 8 9 10 |
greeting := "Hello from Hyderabad! 🇮🇳" empty := "" // zero value fmt.Printf("Greeting: %q (length: %d bytes)\n", greeting, len(greeting)) // "Hello from Hyderabad! 🇮🇳" (length: 30 bytes — emoji takes 4) |
4. Two Very Important Aliases (Used Everywhere!)
| Alias | Real type | Purpose / Why it exists |
|---|---|---|
| byte | uint8 | Raw byte data, binary, ASCII — clearer than uint8 |
| rune | int32 | Unicode code point (one character) — clearer than int32 |
|
0 1 2 3 4 5 6 7 8 9 |
var b byte = 'A' // 65 (ASCII) var r rune = 'హ' // Telugu letter Ha — Unicode value 3129 fmt.Printf("byte: %d (%c)\n", b, b) // 65 (A) fmt.Printf("rune: %d (%c)\n", r, r) // 3129 (హ) |
5. Quick Summary Table — All Basic Types at a Glance
| Group | Type(s) | Zero Value | Most Common Choice | Notes |
|---|---|---|---|---|
| Boolean | bool | false | bool | — |
| Signed int | int, int8/16/32/64 | 0 | int | Platform size (usually 64-bit) |
| Unsigned int | uint, uint8/16/32/64 | 0 | uint, uint8 (byte) | No negatives |
| Float | float32, float64 | 0.0 | float64 | Use float64 almost always |
| Complex | complex64, complex128 | 0+0i | complex128 | Rare outside math/science |
| String | string | “” | string | UTF-8, immutable |
| Aliases | byte (=uint8), rune (=int32) | 0 | byte, rune | Semantic clarity |
6. Your Quick Practice Exercise
Create basic_types.go and experiment:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package main import "fmt" func main() { var ( active bool = true count int = 42 small int8 = -100 pi float64 = 3.1415926535 greeting string = "Namaste Hyderabad!" b byte = 'G' emoji rune = '🚀' ) fmt.Printf("bool: %t\n", active) fmt.Printf("int: %d int8: %d\n", count, small) fmt.Printf("float64: %.4f\n", pi) fmt.Printf("string: %q (len: %d)\n", greeting, len(greeting)) fmt.Printf("byte: %d (%c) rune: %d (%c)\n", b, b, emoji, emoji) } |
Run it — change values, try overflow (int8 = 200 → compile error!), play with negative/positive.
Any confusion left?
- Difference between int vs int64 in practice?
- When to use rune vs byte?
- How strings handle Unicode/emoji?
- Or next: zero values deeper? type inference? constants again?
You’re doing amazing — keep typing and running these examples! 💻🇮🇳 Let’s keep the momentum! 🚀
