Numeric Types, Strings, Runes & Byte Slices
Go's built-in types separate fixed-width numbers, UTF-8 text, Unicode code points (runes), and raw bytes.
Mixing them without conversion is a common source of off-by-one and encoding bugs.
Summary
Integers and floats have explicit sizes; untyped constants adapt to context.
Strings are immutable UTF-8 byte sequences.
A rune is an alias for int32 representing a Unicode code point.
Use []byte for binary data and []rune when you need character-indexed text operations.
Recipe
Quick-reference recipe card - copy-paste ready.
package main
import (
"fmt"
"strconv"
"unicode/utf8"
)
func main() {
s := "café"
fmt.Println(utf8.RuneCountInString(s)) // rune count: 4
n, err := strconv.Atoi("42")
if err != nil {
panic(err)
}
fmt.Println(n + 1)
}When to reach for this:
- Count characters with
utf8.RuneCountInString, notlen(string). - Parse user input with
strconv, notfmt.Sscanf, for clear errors. - Convert numbers explicitly with
int64(x)when widening or narrowing. - Treat
stringand[]byteas distinct unless copying for APIs.
Working Example
package main
import (
"fmt"
"strconv"
"unicode/utf8"
)
func main() {
price := "19.99"
amount, err := strconv.ParseFloat(price, 64)
if err != nil {
fmt.Println("parse error:", err)
return
}
title := "Hello, 世界"
fmt.Println("bytes:", len(title))
fmt.Println("runes:", utf8.RuneCountInString(title))
for i, r := range title {
fmt.Printf("byte index %d: %U\n", i, r)
}
b := []byte(title)
b[0] = 'h' // mutates copy only if you reassign string from b
fmt.Println(string(b))
}What this demonstrates:
strconv.ParseFloatfor string-to-number conversion with errors.lenon strings measures UTF-8 bytes, not runes.rangeover strings decodes runes and yields byte indices.[]byte(string)allocates a mutable copy of bytes.
Deep Dive
How It Works
- Numeric types are signed/unsigned integers (
int,int8..int64,uint,uint8..uint64), floats (float32,float64), andcomplex64/complex128. intanduintsize is platform-dependent (32 or 64 bit); prefer sized types for serialization.- Strings are read-only; converting to
[]bytecopies unless using unsafe (avoid). - UTF-8 encodes runes as 1-4 bytes; invalid UTF-8 is replaced with
U+FFFDwhen ranging.
Numeric Types at a Glance
| Type | Typical use | Overflow |
|---|---|---|
int | indexes, lengths | wraps in unchecked arithmetic |
int64 | IDs, timestamps | same |
uint8 | raw bytes | same |
float64 | measurements | IEEE rounding errors |
big.Int (package) | arbitrary precision | not built-in |
String, Rune, Byte
| Operation | Code | Result |
|---|---|---|
| Byte length | len(s) | UTF-8 byte count |
| Rune count | utf8.RuneCountInString(s) | Unicode code points |
| Iterate runes | for i, r := range s | byte index + rune |
| Byte slice | []byte(s) | mutable copy |
| Rune slice | []rune(s) | one rune per code point |
Go Notes
// strconv is the idiomatic string/number bridge
i, err := strconv.ParseInt("ff", 16, 64)
f := strconv.FormatFloat(3.5, 'f', 2, 64)
// rune literal vs byte
var letter rune = 'A' // int32 code point
var raw byte = 'A' // uint8 ASCIIGotchas
- Using
lenfor character count -"é"haslen2 in UTF-8. Fix:utf8.RuneCountInStringorrange. - Truncating with
int32(x)silently - Largeint64toint32wraps. Fix: check bounds before conversion. - Mutating
[]bytederived from string - Safe for the slice, but reassigningstring(b)creates new string; original unchanged. Fix: treat conversion as copy. fmt.Sscanffor parsing - Weak error reporting vsstrconv. Fix: preferstrconv.Parse*functions.- Comparing floats with
==-0.1+0.2issues. Fix: epsilon compare or integers in cents. - Indexing string by byte -
s[i]is a byte, not a rune; may split multibyte sequences. Fix: range or convert to[]runefor indexing.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
[]byte | IO, crypto, wire formats | You need character operations |
[]rune | Reverse, insert by character index | Memory matters for huge ASCII text |
strings.Builder | Building strings in loops | Few concatenations (+ is fine) |
math/big | Decimal money, crypto | Simple counters |
FAQs
What is a rune in Go?
type rune = int32 - a Unicode code point.
Rune literals like '世' are typed as untyped rune constants.
Why are strings immutable?
Immutability enables safe sharing and efficient substring slices that reference the same backing array.
Mutations go through []byte or strings.Builder.
How do I convert int to string?
strconv.Itoa(42)
strconv.FormatInt(42, 10)
fmt.Sprintf("%d", 42) // allocates more; fine for logsWhat is the difference between byte and uint8?
They are identical types; byte is an alias for uint8 used for ASCII/binary data.
How does range over string handle invalid UTF-8?
Invalid bytes are replaced with rune utf8.RuneError (U+FFFD) and width 1.
Should I use float64 for money?
No - use integer minor units (cents) or shopspring/decimal / big.Rat for exact arithmetic.
What is an untyped constant?
Constants without explicit type (like 42 or 3.14) take the type demanded by the context with default int/float64/complex128.
How do I trim UTF-8 safely?
Use strings.TrimSpace and utf8.ValidString when validating user text.
Avoid trimming by raw byte counts for display widths.
Can I range over []byte as runes?
for _, b := range []byte(s) yields bytes, not runes.
Use for _, r := range string(b) or utf8.DecodeRune.
What does strconv.Atoi do?
Shortcut for ParseInt(s, 10, 0) returning int.
It errors on overflow for the platform int size.
How do I concatenate strings efficiently?
Use strings.Builder in loops; the compiler optimizes simple + chains of few parts.
Are string and []byte interchangeable in JSON?
json.Marshal encodes both as strings or base64 depending on field type.
Pick one type in your API structs deliberately.
Related
- Arrays, Slices & Maps at a Glance - Slice mechanics behind
[]byteand[]rune - Zero Values and Initialization - Empty string and numeric defaults
- Variables, Constants & Scope - Typed vs untyped constants
- Go Fundamentals Basics - String iteration example
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).