Go: Convert Rune to String and String to Rune — Complete Guide (2026)
To convert a rune to a string in Go, use string(r). To convert a string to a slice of runes, use []rune(s). These two conversions are built into the language — no import required.
r := 'A'
s := string(r) // "A"
str := "hello"
runes := []rune(str) // [104 101 108 108 111]
Last updated: March 2026
What Is a Rune in Go?
A rune is an alias for int32. It represents a single Unicode code point. Go source files are UTF-8 by default, so any character literal you write between single quotes — 'A', '€', '日' — is a rune.
var r rune = '€'
fmt.Printf("type: %T, value: %d, char: %c\n", r, r, r)
// type: int32, value: 8364, char: €
The key insight: a rune is a number. 'A' is 65. '€' is 8364. Converting a rune to a string produces the UTF-8 encoding of that code point, not its decimal representation.
Convert Rune to String
Method 1: string(r) — the standard approach
r := '日'
s := string(r)
fmt.Println(s) // 日
fmt.Println(len(s)) // 3 (bytes, not characters — UTF-8 encodes 日 as 3 bytes)
string(r) takes the rune's Unicode code point and returns its UTF-8 byte sequence as a Go string.
Method 2: fmt.Sprintf with %c
r := 'Go'[0] // won't compile — string indexing yields bytes, not runes
r2 := '€'
s := fmt.Sprintf("%c", r2)
fmt.Println(s) // €
Use %c when you need to embed a rune inside a larger formatted string.
Method 3: string([]rune{r})
r := '☺'
s := string([]rune{r})
fmt.Println(s) // ☺
This is equivalent to string(r) but makes it explicit that you are building a one-rune string from a rune slice.
Convert String to Rune Slice
Use []rune(s) to convert a string into a slice where each element is one Unicode code point.
s := "Hello, 世界"
runes := []rune(s)
fmt.Println(len(s)) // 13 — byte count
fmt.Println(len(runes)) // 9 — character count
for i, r := range runes {
fmt.Printf("[%d] %c (%d)\n", i, r, r)
}
This is the correct way to index into a string by character position rather than byte position.
Iterating a String with for range — You Already Get Runes
Go's for range over a string automatically decodes UTF-8 and yields (byteIndex, rune) pairs:
s := "café"
for i, r := range s {
fmt.Printf("byte index %d: %c (U+%04X)\n", i, r, r)
}
// byte index 0: c (U+0063)
// byte index 1: a (U+0061)
// byte index 2: f (U+0066)
// byte index 3: é (U+00E9) ← 'é' is 2 bytes, but you get one rune
Note that i is the byte index, not the character index. If you need the character index, count yourself or use []rune.
Rune vs Byte Comparison
| Operation | Bytes ([]byte) |
Runes ([]rune) |
|---|---|---|
| Type of element | uint8 (0–255) |
int32 (any Unicode code point) |
| Handles non-ASCII | No — multi-byte chars split | Yes — each char is one element |
len(s) after conversion |
Number of bytes | Number of characters |
Index s[i] |
Byte at position i | Code point at character position i |
| Use case | Binary data, ASCII only, HTTP headers | Human-readable text, internationalization |
s := "日本語"
bytes := []byte(s)
fmt.Println(len(bytes)) // 9 (3 bytes per kanji)
runes := []rune(s)
fmt.Println(len(runes)) // 3 (3 characters)
Counting Characters with utf8.RuneCountInString
When you just need the character count without creating a []rune, use utf8.RuneCountInString:
import "unicode/utf8"
s := "日本語"
fmt.Println(len(s)) // 9 (bytes)
fmt.Println(utf8.RuneCountInString(s)) // 3 (characters)
This is more memory-efficient than len([]rune(s)) because it does not allocate a new slice.
The unicode/utf8 package also provides:
utf8.RuneLen(r rune) int— number of bytes needed to encode a runeutf8.ValidString(s string) bool— check if a string contains valid UTF-8utf8.DecodeRuneInString(s string) (rune, int)— decode the first rune and return its byte width
r := '€'
fmt.Println(utf8.RuneLen(r)) // 3
fmt.Println(utf8.ValidString("hello")) // true
fmt.Println(utf8.ValidString("\xff\xfe")) // false
Practical Example: Reverse a Unicode String
The classic "reverse a string" exercise breaks if you reverse bytes instead of runes:
// WRONG — breaks multi-byte characters
func reverseBad(s string) string {
b := []byte(s)
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
return string(b)
}
// CORRECT — swap runes
func reverseString(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
fmt.Println(reverseString("Hello, 世界")) // 界世 ,olleH
Practical Example: Extract a Substring by Character Position
func substrByRune(s string, start, end int) string {
runes := []rune(s)
return string(runes[start:end])
}
s := "Hello, 世界!"
fmt.Println(substrByRune(s, 7, 9)) // 世界
Common Mistakes
Mistake 1: Converting int to string gives a character, not a digit
n := 65
s := string(n) // "A" — not "65"!
s2 := strconv.Itoa(n) // "65" — correct for numbers
If you need to convert a number to its string representation, use strconv.Itoa (for int) or strconv.FormatInt. See the Go string to int conversion guide for all conversion methods.
Mistake 2: Using len(s) for character count
s := "日本語"
fmt.Println(len(s)) // 9 — BYTES, not characters
Always use utf8.RuneCountInString(s) or len([]rune(s)) when you need the character count.
Mistake 3: Indexing a string directly for a character
s := "café"
fmt.Println(s[3]) // 195 — a BYTE, not the rune 'é'
Use []rune(s)[3] or a for range loop to get the rune at a character position.
Related Pages
- Convert Rune to Int in Go — how to get the numeric value of a rune digit
- Convert String to Int in Go —
strconv.Atoi,ParseInt, and all int conversion methods
Summary
| Goal | Code |
|---|---|
| Rune to string | string(r) |
| String to rune slice | []rune(s) |
| Character count | utf8.RuneCountInString(s) |
| Iterate characters | for i, r := range s |
| Rune byte width | utf8.RuneLen(r) |
| Number to string | strconv.Itoa(n) (not string(n)) |