Go: Convert String to Int (and Int to String) — Complete Guide (2026)
To convert a string to an int in Go, use strconv.Atoi(s). It returns (int, error) — always check the error. To convert an int back to a string, use strconv.Itoa(n).
n, err := strconv.Atoi("42")
if err != nil {
log.Fatal(err)
}
fmt.Println(n + 1) // 43
s := strconv.Itoa(42)
fmt.Println(s + "!") // "42!"
Why You Cannot Add Strings and Ints Directly
Go does not perform implicit type conversions. If you try to concatenate a string with a number, you get a compile error:
age := 30
msg := "I am " + age + " years old"
// invalid operation: "I am " + age (mismatched types string and int)
You must convert explicitly. This guide covers every conversion method with when to use each.
String to Int: strconv.Atoi
Atoi stands for "ASCII to integer". It converts a decimal string to int (the platform-native integer size — 64-bit on 64-bit systems).
package main
import (
"fmt"
"strconv"
)
func main() {
s := "123"
n, err := strconv.Atoi(s)
if err != nil {
fmt.Printf("conversion failed: %v\n", err)
return
}
fmt.Println(n + 1) // 124
}
What errors look like:
n, err := strconv.Atoi("abc")
// err: strconv.Atoi: parsing "abc": invalid syntax
n, err = strconv.Atoi("99999999999999999999")
// err: strconv.Atoi: parsing "99999999999999999999": value out of range
When to use Atoi: When you need a plain int from a decimal string. This covers 90% of use cases — parsing command-line arguments, reading config values, processing CSV fields.
String to Int64: strconv.ParseInt
ParseInt offers full control: you specify the base (2–36) and the bit size (0, 8, 16, 32, 64).
package main
import (
"fmt"
"strconv"
)
func main() {
// Decimal (base 10), fit into int64
n, err := strconv.ParseInt("9876543210", 10, 64)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(n) // 9876543210
// Hexadecimal (base 16)
h, _ := strconv.ParseInt("1a2b", 16, 64)
fmt.Println(h) // 6699
// Binary (base 2)
b, _ := strconv.ParseInt("1010", 2, 64)
fmt.Println(b) // 10
// Octal (base 8)
o, _ := strconv.ParseInt("0755", 8, 64)
fmt.Println(o) // 493
}
Bit size parameter:
- 0 — infer from the string prefix (0x → 16, 0 → 8, else 10), fit into int
- 32 — value must fit in int32; returned as int64 (cast to int32 is safe)
- 64 — value must fit in int64
For unsigned integers use strconv.ParseUint:
u, err := strconv.ParseUint("255", 10, 8) // fits in uint8
fmt.Println(uint8(u)) // 255
String to Float: strconv.ParseFloat
f, err := strconv.ParseFloat("3.14159", 64)
if err != nil {
log.Fatal(err)
}
fmt.Println(f * 2) // 6.28318
Int to String: strconv.Itoa
Itoa is the reverse of Atoi — integer to ASCII string, base 10.
n := 42
s := strconv.Itoa(n)
fmt.Println(s) // "42"
fmt.Println(s + "px") // "42px"
This is the fastest and most idiomatic way to convert int to string in Go.
Int to String: strconv.FormatInt
FormatInt handles any base and any int64:
// Decimal
fmt.Println(strconv.FormatInt(255, 10)) // "255"
// Hexadecimal
fmt.Println(strconv.FormatInt(255, 16)) // "ff"
// Binary
fmt.Println(strconv.FormatInt(255, 2)) // "11111111"
// Octal
fmt.Println(strconv.FormatInt(255, 8)) // "377"
Int to String: fmt.Sprintf
fmt.Sprintf is the most flexible — it formats any combination of values into a string using format verbs:
n := 42
s := fmt.Sprintf("%d", n) // "42"
s2 := fmt.Sprintf("count: %d items", n) // "count: 42 items"
s3 := fmt.Sprintf("%05d", n) // "00042" (zero-padded, width 5)
s4 := fmt.Sprintf("%x", n) // "2a" (hex lowercase)
s5 := fmt.Sprintf("%X", n) // "2A" (hex uppercase)
s6 := fmt.Sprintf("%b", n) // "101010" (binary)
s7 := fmt.Sprintf("%08b", n) // "00101010" (binary, 8 wide)
When to use fmt.Sprintf vs strconv.Itoa:
- Use strconv.Itoa when you only need the number as a string — it is faster and allocates less.
- Use fmt.Sprintf when you need the number embedded in a larger string or formatted with padding/precision.
Common Mistake: string(int) is NOT a String Conversion
A frequent mistake by Go beginners:
n := 65
s := string(n) // s is "A", not "65"!
fmt.Println(s) // "A"
string(n) interprets the integer as a Unicode code point and returns the character at that code point. 65 is the Unicode code point for 'A'. This is rarely what you want.
The correct way:
n := 65
s := strconv.Itoa(n) // "65"
fmt.Println(s) // "65"
Error Handling Patterns
Pattern 1: Fatal on bad input (CLI tools)
func mustAtoi(s string) int {
n, err := strconv.Atoi(s)
if err != nil {
log.Fatalf("invalid number %q: %v", s, err)
}
return n
}
port := mustAtoi(os.Args[1])
Pattern 2: Default value on bad input
func atoiOrDefault(s string, def int) int {
n, err := strconv.Atoi(s)
if err != nil {
return def
}
return n
}
timeout := atoiOrDefault(os.Getenv("TIMEOUT_SECS"), 30)
Pattern 3: Collect all errors
type parseError struct {
field string
err error
}
func parseConfig(m map[string]string) (int, int, []parseError) {
var errs []parseError
port, err := strconv.Atoi(m["port"])
if err != nil {
errs = append(errs, parseError{"port", err})
}
workers, err := strconv.Atoi(m["workers"])
if err != nil {
errs = append(errs, parseError{"workers", err})
}
return port, workers, errs
}
Base Conversions Between Strings
// Hex string "ff" → decimal int 255
n, _ := strconv.ParseInt("ff", 16, 64)
fmt.Println(n) // 255
// Decimal int 255 → hex string "ff"
s := strconv.FormatInt(255, 16)
fmt.Println(s) // "ff"
// Decimal string "255" → binary string "11111111"
n2, _ := strconv.ParseInt("255", 10, 64)
fmt.Println(strconv.FormatInt(n2, 2)) // "11111111"
Method Comparison Table
| Conversion | Function | Returns | Notes |
|---|---|---|---|
string → int |
strconv.Atoi(s) |
(int, error) |
Decimal only, most common |
string → int64 |
strconv.ParseInt(s, base, bits) |
(int64, error) |
Any base 2–36 |
string → uint64 |
strconv.ParseUint(s, base, bits) |
(uint64, error) |
Unsigned variant |
string → float64 |
strconv.ParseFloat(s, bits) |
(float64, error) |
32 or 64 bit |
int → string |
strconv.Itoa(n) |
string |
Fastest, decimal only |
int64 → string |
strconv.FormatInt(n, base) |
string |
Any base 2–36 |
uint64 → string |
strconv.FormatUint(n, base) |
string |
Unsigned variant |
any → string |
fmt.Sprintf("%d", n) |
string |
Flexible, slightly slower |
int → rune char |
string(rune(n)) |
string |
Unicode code point, not the digit |
FAQ
Q: What is the difference between strconv.Atoi and strconv.ParseInt?
A: Atoi(s) is equivalent to ParseInt(s, 10, 0) — base 10, fit into the native int size. ParseInt is more flexible: it handles hex, binary, octal, and lets you specify the bit size of the result. Use Atoi for ordinary decimal strings; use ParseInt when you need other bases or explicit sizing.
Q: Why does string(42) give me "*" instead of "42"?
A: In Go, string(intValue) converts the integer to the Unicode character at that code point. Code point 42 is *. To convert the number to its decimal string representation, use strconv.Itoa(42) which gives "42".
Q: What happens if the string has leading/trailing spaces?
A: strconv.Atoi(" 42 ") returns an error — strconv functions do not trim whitespace. Use strings.TrimSpace(s) first: strconv.Atoi(strings.TrimSpace(s)).