Question
i := 123
s := string(i)
In Go, s becomes a single-character string such as '{' or another Unicode character, but I want the string value "123" instead.
How can I correctly convert an int to its decimal string representation in Go?
Short Answer
By the end of this page, you will understand why string(i) does not convert an integer like 123 into the text "123" in Go. You will learn the correct way to do this using the strconv package, when to use strconv.Itoa vs strconv.FormatInt, and how integer-to-string conversion is commonly handled in real Go code.
Concept
In Go, converting a numeric value to a string depends on what kind of conversion you want.
When you write:
s := string(i)
Go does not interpret this as “turn the number into its digits.” Instead, it treats i as a Unicode code point and creates a string containing the character represented by that code point.
For example:
fmt.Println(string(65))
outputs:
A
because 65 is the Unicode code point for A.
So if i := 123, then string(i) gives the character with code point 123, which is:
{
That is why string(i) does not produce "123".
To convert an integer to its decimal text form, use the strconv package:
strconv.Itoa(i)
This returns the string form of the number, such as "123".
Why this matters
This distinction is important in real programs because numbers and text are used differently:
- Numbers are used for calculations.
- Strings are used for display, logging, JSON fields, URLs, IDs, and user output.
Using the wrong conversion can lead to confusing bugs, strange characters, or invalid output.
Mental Model
Think of an int in Go as a number in a box.
There are two different ways to interpret that box:
- As a number to display →
123should become"123" - As a character code →
123should become the character whose Unicode value is 123, which is{
So:
strconv.Itoa(123)means: write the number using digitsstring(123)means: treat 123 as a character code
A simple analogy:
strconv.Itoa(123)is like writing the label 123 on paper.string(123)is like looking up item 123 in a character table and returning the symbol found there.
Syntax and Examples
The most common way to convert an int to a string in Go is with strconv.Itoa.
package main
import (
"fmt"
"strconv"
)
func main() {
i := 123
s := strconv.Itoa(i)
fmt.Println(s)
}
Output:
123
Using strconv.FormatInt
If you are working with int64, or you want to specify the base manually, use strconv.FormatInt.
package main
import (
"fmt"
"strconv"
)
func main() {
var n int64 = 123
s := strconv.FormatInt(n, 10)
fmt.Println(s)
}
Output:
123
The second argument, 10, means decimal format.
What string(i) actually does
package main
import "fmt"
func main() {
i := 65
s := string(i)
fmt.Println(s)
}
Output:
A
This is useful only when you intentionally want a character from a Unicode code point.
Quick rule
- Use
strconv.Itoa(i)forint - Use
strconv.FormatInt(n, 10)forint64 - Use
string(i)only when converting a code point to a character string
Step by Step Execution
Consider this example:
package main
import (
"fmt"
"strconv"
)
func main() {
i := 123
s := strconv.Itoa(i)
fmt.Println(s)
}
Step by step:
-
i := 123- A variable named
iis created. - Its value is the integer
123.
- A variable named
-
s := strconv.Itoa(i)- Go calls the
Itoafunction from thestrconvpackage. Itoareads the integer value123.- It converts that number into the string
"123". - The result is stored in
s.
- Go calls the
-
fmt.Println(s)- Go prints the contents of
s. - Output is
123.
- Go prints the contents of
Now compare that with this:
package main
import "fmt"
func main() {
i := 123
s := string(i)
fmt.Println(s)
}
Step by step:
-
i := 123iholds the number123.
-
s := string(i)- Go does not write the digits
1,2, and3. - It treats
123as a Unicode code point. - Unicode code point
123is{. - So
sbecomes"{".
- Go does not write the digits
-
fmt.Println(s)- Output is
{.
- Output is
That is the key difference.
Real World Use Cases
Integer-to-string conversion is very common in Go programs.
Building log messages
userID := 42
msg := "processing user " + strconv.Itoa(userID)
Creating URLs or query parameters
page := 3
url := "/articles?page=" + strconv.Itoa(page)
Writing values to JSON-like text or CSV
count := 15
line := "items," + strconv.Itoa(count)
Displaying numbers in CLI tools
files := 7
fmt.Println("Processed " + strconv.Itoa(files) + " files")
Generating cache keys or map keys
id := 1001
key := "user:" + strconv.Itoa(id)
In all of these cases, the goal is to represent the number as readable text, not as a character code.
Real Codebase Usage
In real Go codebases, developers usually convert integers to strings in a few common ways depending on the context.
1. Simple conversion with strconv.Itoa
For ordinary int values:
idStr := strconv.Itoa(id)
This is the most direct and readable choice.
2. Formatting inside output with fmt.Sprintf
Sometimes developers use formatted strings when building larger messages:
msg := fmt.Sprintf("user id=%d created", id)
This is helpful when multiple values are involved.
3. Validation and early returns
A converted string may be used after checking input or constraints:
func userPath(id int) string {
if id <= 0 {
return ""
}
return "/users/" + strconv.Itoa(id)
}
This pattern combines validation with a clear conversion step.
4. Configuration and environment values
Applications often need numeric values in string form when working with environment variables, command output, headers, or URLs.
port := 8080
address := ":" + strconv.Itoa(port)
5. Error messages and debugging
if retries > maxRetries {
return fmt.Errorf("too many retries: %d", retries)
}
In practice, developers choose:
strconv.Itoafor direct conversionfmt.Sprintfwhen formatting a larger stringstring(runeValue)only when intentionally working with characters or Unicode
Common Mistakes
Mistake 1: Using string(i) for digit conversion
Broken code:
i := 123
s := string(i)
fmt.Println(s)
This does not print 123. It prints the character for Unicode code point 123.
Correct code:
i := 123
s := strconv.Itoa(i)
fmt.Println(s)
Mistake 2: Forgetting to import strconv
Broken code:
package main
import "fmt"
func main() {
i := 123
s := strconv.Itoa(i)
fmt.Println(s)
}
This fails because strconv was not imported.
Correct code:
import (
"fmt"
"strconv"
)
Mistake 3: Mixing int and int64 carelessly
Broken code:
var n int64 = 123
s := strconv.Itoa(n)
strconv.Itoa expects an int, not int64.
Correct options:
s := strconv.FormatInt(n, 10)
or, if safe and appropriate:
s := strconv.Itoa(int(n))
Mistake 4: Using fmt.Sprintf when only conversion is needed
This is valid:
s := fmt.Sprintf("%d", i)
But for simple int to string conversion, this is usually clearer:
s := strconv.Itoa(i)
Mistake 5: Confusing bytes, runes, and strings
A number can represent different things in Go:
- a numeric value
- a byte
- a rune (Unicode code point)
So this:
string(49)
returns:
"1"
because 49 is the code point for the character 1, not because Go converted the number 49 into its decimal text automatically.
Comparisons
| Approach | Use case | Example | Result for 123 |
|---|---|---|---|
strconv.Itoa(i) | Convert int to decimal string | strconv.Itoa(123) | "123" |
strconv.FormatInt(n, 10) | Convert int64 to decimal string | strconv.FormatInt(123, 10) | "123" |
fmt.Sprintf("%d", i) | Format values into a larger string | fmt.Sprintf("%d", 123) | "123" |
string(i) | Convert Unicode code point to string | string(123) | "{" |
strconv.Itoa vs fmt.Sprintf
| Feature | strconv.Itoa | fmt.Sprintf |
|---|---|---|
| Purpose | Simple int-to-string conversion | General string formatting |
| Best for | Direct conversion | Building formatted messages |
| Readability | Very clear for this task | Better when formatting multiple values |
| Performance | Typically more direct | More flexible, but heavier |
Rule of thumb
- Need only the digits of an
int? Usestrconv.Itoa. - Need to format a sentence or multiple values? Use
fmt.Sprintf. - Need a character from a code point? Use
string(i).
Cheat Sheet
import "strconv"
Convert int to string
s := strconv.Itoa(i)
Convert int64 to string
s := strconv.FormatInt(n, 10)
Convert uint64 to string
s := strconv.FormatUint(n, 10)
Format with fmt
s := fmt.Sprintf("%d", i)
Important rule
string(123)
means:
- convert Unicode code point
123to a string - result:
"{"
It does not mean decimal conversion.
Common choices
strconv.Itoa(i)→ best forintstrconv.FormatInt(n, 10)→ best forint64fmt.Sprintf("%d", i)→ useful for formatted outputstring(i)→ only for rune/code point conversion
FAQ
Why does string(123) not return "123" in Go?
Because string(123) treats 123 as a Unicode code point and converts it to the corresponding character, which is {.
What is the correct way to convert an int to a string in Go?
Use:
strconv.Itoa(i)
When should I use strconv.FormatInt instead of strconv.Itoa?
Use strconv.FormatInt when your value is int64 or when you want to choose a base such as binary, decimal, or hexadecimal.
Is fmt.Sprintf("%d", i) also valid?
Yes. It works well, especially when building larger formatted strings. For simple conversion, strconv.Itoa is usually clearer.
What does string(65) return in Go?
It returns "A" because 65 is the Unicode code point for A.
Can I convert negative integers with strconv.Itoa?
Yes.
strconv.Itoa(-42) // "-42"
Does Go automatically convert numbers to strings?
No. Go is explicit about type conversion, so you usually need strconv or fmt for numeric text output.
Mini Project
Description
Build a small Go program that creates user profile URLs from numeric user IDs. This demonstrates the practical need to convert integers into strings when building text values such as paths, keys, and messages.
Goal
Create a program that converts integer user IDs into strings and uses them to build readable profile URLs.
Requirements
- Read or define at least three integer user IDs.
- Convert each
intID to a string correctly. - Build a URL in the form
/users/<id>for each ID. - Print each generated URL.
- Do not use
string(id)for the conversion.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.