Basic Types
Go's built-in types fall into a few families: booleans, strings, integers of several sizes, floating-point numbers and complex numbers. This lesson tours each family, explains which type to choose, and shows what happens at the edges — such as an integer that overflows.
Overview
| Family | Types | Example values |
|---|---|---|
| Boolean | bool | true, false |
| String | string | "hello", `raw text` |
| Signed integers | int, int8, int16, int32, int64 | -7, 0, 1_000_000 |
| Unsigned integers | uint, uint8, uint16, uint32, uint64, uintptr | 0, 255 |
| Aliases | byte (= uint8), rune (= int32) | 'A', 'é' |
| Floating point | float32, float64 | 3.14, 1e-9 |
| Complex | complex64, complex128 | 2 + 3i |
Booleans
A bool is true or false. Comparisons produce booleans, and &&, || and ! combine them. Unlike some languages, Go never treats numbers, strings or nil as true or false — conditions must be real booleans:
package main import "fmt" func main() { age := 20 hasTicket := true canEnter := age >= 18 && hasTicket fmt.Println(canEnter, !canEnter) }
true false
Strings
A string is an immutable sequence of bytes, normally holding UTF-8 text. Double-quoted strings understand escapes such as \n and \t; backtick "raw" strings take everything literally and can span lines:
package main import "fmt" func main() { greeting := "Hello,\tworld\n" path := `C:\Users\go\notes.txt` poem := `Roses are red, Gophers are blue.` fmt.Print(greeting) fmt.Println(path) fmt.Println(poem) fmt.Println(len("Go"), len("Café")) }
Hello, world C:\Users\go\notes.txt Roses are red, Gophers are blue. 2 5
len counts bytes, not characters: é takes two bytes in UTF-8, so "Café" has length 5. Lesson 19 explains bytes, runes and Unicode properly.
Integers
The number in a type's name is its size in bits, which decides its range. int and uint are 64 bits on all modern 64-bit systems (32 bits on 32-bit ones). The math package has constants for each limit:
package main import ( "fmt" "math" ) func main() { fmt.Println("int8: ", math.MinInt8, "to", math.MaxInt8) fmt.Println("uint8: ", 0, "to", math.MaxUint8) fmt.Println("int16: ", math.MinInt16, "to", math.MaxInt16) fmt.Println("int32: ", math.MinInt32, "to", math.MaxInt32) fmt.Println("int64: ", math.MinInt64, "to", math.MaxInt64) fmt.Println("int: ", math.MinInt, "to", math.MaxInt) fmt.Println("uint64:", 0, "to", uint64(math.MaxUint64)) }
int8: -128 to 127 uint8: 0 to 255 int16: -32768 to 32767 int32: -2147483648 to 2147483647 int64: -9223372036854775808 to 9223372036854775807 int: -9223372036854775808 to 9223372036854775807 uint64: 0 to 18446744073709551615
Use int for ordinary counting, indexes and lengths — it's what len returns and what the standard library expects. Reach for a sized type only when a file format, network protocol or memory budget requires one: uint8/byte for raw bytes, int64 for values such as file sizes or timestamps that need to be 64 bits everywhere.
Integer literals can be written in several bases:
package main import "fmt" func main() { fmt.Println(255, 0xFF, 0o377, 0b1111_1111) // decimal, hex, octal, binary }
255 255 255 255
Overflow
When arithmetic on an integer variable goes past the type's range, it silently wraps around. The compiler can only catch it when the values are constants:
package main import "fmt" func main() { var level uint8 = 250 level += 10 fmt.Println(level) var small int8 = 127 small++ fmt.Println(small) }
4 -128
package main import "fmt" func main() { var level uint8 = 256 fmt.Println(level) }
./main.go:6:20: cannot use 256 (untyped int constant) as uint8 value in variable declaration (overflows)
Wrapping is rarely what you want. Pick a type that's comfortably big enough — another reason to default to int.
Floating-point numbers
float64 has about 15–16 significant decimal digits; float32 about 7. Use float64 unless you have a reason not to — it's the default for literals with a decimal point and what the math package uses:
package main import "fmt" func main() { var f32 float32 = 1.0 / 3 f64 := 1.0 / 3 fmt.Println(f32) fmt.Println(f64) a, b := 0.1, 0.2 fmt.Println(a+b, a+b == 0.3) fmt.Println(6.02e23, 1.5e-3) }
0.33333334 0.3333333333333333 0.30000000000000004 false 6.02e+23 0.0015
Like every language using binary floating point, Go can't store 0.1 exactly, so small rounding errors appear. (Interestingly, writing 0.1+0.2 == 0.3 directly gives true: arithmetic on constants is done exactly at compile time, as lesson 10 explains. The error appears once the values live in float64 variables.) Never compare floats with == after arithmetic, and don't use them for money — store cents in an int64 instead.
Complex numbers
Go has complex numbers built in, written with an i suffix. They're mostly used in scientific and signal-processing code:
package main import ( "fmt" "math/cmplx" ) func main() { z := 3 + 4i fmt.Println(z, real(z), imag(z), cmplx.Abs(z)) fmt.Printf("%T\n", z) }
(3+4i) 3 4 5 complex128
Choosing a type
| For… | Use |
|---|---|
| Counts, indexes, loop variables, sizes | int |
| Measurements and calculations with fractions | float64 |
| Money | int64 holding the smallest unit (cents) |
| Raw binary data | byte (usually in a []byte slice) |
| A single Unicode character | rune |
| Text | string |
Try it yourself
Predict what each line prints, then run it to check. What happens if you change uint8 to int?
stock: 254 price: 19.99 int32 float64 int
math package.