Go Data Types
A data type defines what kind of value a variable can hold, how much memory it occupies, and what operations are valid on it. Because Go is statically typed, every variable's type is fixed once declared — you cannot later assign a string to a variable that was declared as an int.
What Data Types Determine
- The category of data — number, text, true/false, and so on.
- The amount of memory allocated to store the value.
- The operations you're allowed to perform (you can add two
intvalues, but not anintand astring, without an explicit conversion).
The Three Basic Categories
Go's basic types fall into three groups: boolean, numeric, and string.
1. Boolean (bool)
Holds exactly one of two values: true or false.
2. Numeric Types
- Integers:
int,int8,int16,int32,int64 - Unsigned integers:
uint,uint8,uint16,uint32,uint64 - Floating-point:
float32,float64 - Complex numbers:
complex64,complex128(used for scientific and engineering computation; rarely needed in typical application code)
3. String (string)
A sequence of characters (text), written between double quotes.
package main
import "fmt"
func main() {
isAvailable := true
quantity := 12
price := 49.99
productName := "Wireless Mouse"
fmt.Println("Available:", isAvailable)
fmt.Println("Quantity:", quantity)
fmt.Println("Price:", price)
fmt.Println("Product:", productName)
}
Type Inference
When you use := or omit an explicit type with var, Go infers the type from the value:
package main
import "fmt"
func main() {
count := 100 // inferred as int
rating := 4.5 // inferred as float64
message := "Hello" // inferred as string
fmt.Printf("%T\n", count)
fmt.Printf("%T\n", rating)
fmt.Printf("%T\n", message)
}
Expected output:
int
float64
string
Why Data Types Matter
- They let the compiler catch mismatched-type mistakes before the program ever runs.
- They let Go allocate exactly the right amount of memory for a value.
- They document intent — a function that takes a
uintis telling its caller "this should never be negative."
Boolean Data Type
A bool variable is declared with the bool type, and Go's usual type inference applies here too:
package main
import "fmt"
func main() {
var isActive bool = true // explicit type
var isVerified = false // type inferred
var isCompleted bool // no initial value — defaults to false
isAvailable := true // short declaration
fmt.Println("Active:", isActive)
fmt.Println("Verified:", isVerified)
fmt.Println("Completed:", isCompleted)
fmt.Println("Available:", isAvailable)
}
An uninitialized bool always defaults to false — Go never leaves it undefined. Booleans are most often produced by comparisons rather than typed directly:
package main
import "fmt"
func main() {
age := 18
isAdult := age >= 18
hasPermission := false
fmt.Println("Is adult:", isAdult)
fmt.Println("Has permission:", hasPermission)
}
Integer Types
Go integers store whole numbers — values with no decimal point — and come in two families: signed (can be negative) and unsigned (zero or positive only).
If you don't specify a size, Go's default integer type is simply int, whose size (32 or 64 bits) depends on the platform the program is compiled for — on virtually all modern desktop and server systems, that means 64 bits.
Signed Integer Types
package main
import "fmt"
func main() {
temperature := -12
score := 98
fmt.Printf("Temperature: %v (Type: %T)\n", temperature, temperature)
fmt.Printf("Score: %v (Type: %T)\n", score, score)
}
| Type | Size | Range |
|---|---|---|
int | Platform-dependent | Typically 64-bit range on modern systems |
int8 | 8 bits | -128 to 127 |
int16 | 16 bits | -32,768 to 32,767 |
int32 | 32 bits | -2,147,483,648 to 2,147,483,647 |
int64 | 64 bits | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
Unsigned Integer Types
Unsigned types store only non-negative values, which makes them a natural fit for things like counts or byte sizes that can never logically be negative:
package main
import "fmt"
func main() {
itemCount := uint(150)
maxUsers := uint16(5000)
fmt.Printf("Items: %v (Type: %T)\n", itemCount, itemCount)
fmt.Printf("Max Users: %v (Type: %T)\n", maxUsers, maxUsers)
}
| Type | Size | Range |
|---|---|---|
uint | Platform-dependent | Typically 64-bit range on modern systems |
uint8 | 8 bits | 0 to 255 |
uint16 | 16 bits | 0 to 65,535 |
uint32 | 32 bits | 0 to 4,294,967,295 |
uint64 | 64 bits | 0 to 18,446,744,073,709,551,615 |
Choosing an Integer Type
- Use plain
intfor general-purpose values — it's the idiomatic default in Go, and most of the standard library expects it. - Reach for a sized type (
int8,int32,uint16, and so on) only when memory layout matters, such as when working with binary file formats, network protocols, or very large slices where every byte counts. - Use an unsigned type only when a negative value would be logically meaningless — and be aware that subtracting past zero on an unsigned type wraps around to a very large number instead of going negative, which is a common source of subtle bugs.
Floating-Point Types
Floating-point types store numbers with a fractional part, such as 3.14, -0.75, or values in scientific notation like 1.2e6.
| Type | Size | Approximate range |
|---|---|---|
float32 | 32-bit | ±3.4 × 10³⁸, roughly 7 significant decimal digits |
float64 | 64-bit | ±1.8 × 10³⁰⁸, roughly 15–17 significant decimal digits |
Go's default floating-point type — used whenever you write a decimal literal without specifying otherwise — is float64.
package main
import "fmt"
func main() {
temperature := float32(36.6)
pressure := float32(101.325)
fmt.Printf("Temperature: %v (Type: %T)\n", temperature, temperature)
fmt.Printf("Pressure: %v (Type: %T)\n", pressure, pressure)
}
package main
import "fmt"
func main() {
distance := 1.496e+11 // roughly the Earth–Sun distance, in meters
pi := 3.141592653589793
fmt.Printf("Distance: %v (Type: %T)\n", distance, distance)
fmt.Printf("Pi value: %.10f (Type: %T)\n", pi, pi)
}
Scientific notation is written with e or E:
package main
import "fmt"
func main() {
smallValue := 5.2e-3 // 0.0052
largeValue := 9.1e+6 // 9,100,000
fmt.Println("Small:", smallValue)
fmt.Println("Large:", largeValue)
}
Choosing between float32 and float64: default to float64 unless you have a specific reason not to — it's what the standard library and most third-party code expect, and it avoids the precision loss float32 introduces. Reach for float32 only when memory is genuinely tight, such as large numeric datasets or graphics buffers.
Important note on precision: like almost every mainstream language, Go's floating-point types cannot represent every decimal value exactly (this is a consequence of binary floating-point representation, defined by the IEEE 754 standard — not a Go-specific limitation). A calculation like
0.1 + 0.2will print as0.30000000000000004, not0.3. For money or other values requiring exact decimal precision, store amounts as integers (e.g., cents) or use a decimal library rather thanfloat64.
String Data Type
A string stores text — a sequence of characters enclosed in double quotes.
package main
import "fmt"
func main() {
var greeting string = "Welcome!"
var emptyMessage string // defaults to ""
title := "Go Programming"
fmt.Printf("Greeting: %q (Type: %T)\n", greeting, greeting)
fmt.Printf("Empty Message: %q (Type: %T)\n", emptyMessage, emptyMessage)
fmt.Printf("Title: %q (Type: %T)\n", title, title)
}
An uninitialized string's zero value is "" — an empty string, which is a valid, usable string, not a null or missing value.
String Characteristics
- Strings are immutable: once created, a string's contents cannot be changed in place. Operations that appear to "modify" a string, like concatenation, actually create a new string.
- Internally, a Go string is a read-only sequence of bytes, conventionally interpreted as UTF-8-encoded text. This matters once you start indexing into strings with non-ASCII characters — a topic covered in more depth once you reach string manipulation and runes.
Concatenation
package main
import "fmt"
func main() {
firstName := "Arjun"
lastName := "Kumar"
fullName := firstName + " " + lastName
fmt.Println("Full Name:", fullName)
}
Raw String Literals
Strings wrapped in backticks (`) instead of double quotes are called raw string literals. They preserve line breaks and ignore escape sequences entirely, which makes them convenient for multi-line text, regular expressions, or embedded JSON/SQL snippets:
package main
import "fmt"
func main() {
message := `This is a
multi-line string
in Go.`
fmt.Println(message)
}
Common mistake: trying to modify a string's characters directly with index assignment, like greeting[0] = 'h'. Because strings are immutable, this does not compile. To change a string's content, build a new string instead — for example, using the strings package or converting to a []byte / []rune, modifying that, and converting back.