O que é Rune em Go?
Uma rune em Go é um alias para o tipo int32 que representa um único code point Unicode. Em termos práticos, uma rune é um “caractere” — seja uma letra, dígito, símbolo, emoji ou qualquer outro glifo definido pelo padrão Unicode.
A definição na standard library é simples:
type rune = int32
Go usa UTF-8 como encoding padrão para strings, onde cada caractere pode ocupar de 1 a 4 bytes. Uma rune representa o valor numérico do code point Unicode de um caractere, independente de quantos bytes ele ocupa em UTF-8. Por exemplo, a letra ‘A’ é a rune 65 (U+0041), o ‘é’ é a rune 233 (U+00E9) e o emoji ‘🚀’ é a rune 128640 (U+1F680).
Essa distinção entre bytes e runes é fundamental para trabalhar corretamente com texto internacionalizado. Enquanto strings operam no nível de bytes, runes operam no nível de caracteres, e confundir os dois é uma das fontes mais comuns de bugs em programas que lidam com texto Unicode.
Rune vs byte
A distinção entre rune e byte é essencial:
// byte = uint8 — um único byte (0-255)
// rune = int32 — um code point Unicode (0-1114111)
texto := "café"
// Acessando como bytes
fmt.Println(len(texto)) // 5 bytes (não 4 caracteres!)
fmt.Println(texto[3]) // 195 (0xC3) — primeiro byte de 'é'
fmt.Println(texto[4]) // 169 (0xA9) — segundo byte de 'é'
// Acessando como runes
runes := []rune(texto)
fmt.Println(len(runes)) // 4 caracteres
fmt.Println(runes[3]) // 233 (U+00E9) — rune 'é' completa
fmt.Printf("%c\n", runes[3]) // é
| Aspecto | byte (uint8) | rune (int32) |
|---|---|---|
| Tamanho | 1 byte | 4 bytes |
| Representa | Um byte | Um code point Unicode |
| Range | 0-255 | 0-1.114.111 |
| Suficiente para | ASCII | Qualquer caractere Unicode |
| Alias de | uint8 | int32 |
Rune literals
Rune literals são escritas com aspas simples:
// Caracteres simples
var a rune = 'A' // 65
var e rune = 'é' // 233
var sigma rune = 'Σ' // 931
// Emojis
var foguete rune = '🚀' // 128640
var coracao rune = '❤' // 10084
// Escape sequences
var newline rune = '\n' // 10
var tab rune = '\t' // 9
var nulo rune = '\000' // 0
// Unicode escapes
var omega rune = '\u03A9' // Ω (U+03A9)
var smiley rune = '\U0001F600' // 😀 (U+1F600)
fmt.Printf("Rune: %c, Valor: %d, Unicode: U+%04X\n", a, a, a)
// Rune: A, Valor: 65, Unicode: U+0041
Range sobre strings entrega runes
O range sobre strings é a forma idiomática de iterar por caracteres em Go, porque ele automaticamente decodifica bytes UTF-8 em runes:
texto := "Olá, 🌍!"
// range decodifica UTF-8 → runes automaticamente
for i, r := range texto {
fmt.Printf("posição_byte=%d rune=%c valor=%d\n", i, r, r)
}
// posição_byte=0 rune=O valor=79
// posição_byte=1 rune=l valor=108
// posição_byte=2 rune=á valor=225
// posição_byte=5 rune=, valor=44
// posição_byte=6 rune= valor=32
// posição_byte=7 rune=🌍 valor=127757
// posição_byte=11 rune=! valor=33
// CUIDADO: note que os índices NÃO são sequenciais!
// 'á' começa na posição 2 e ocupa 2 bytes
// '🌍' começa na posição 7 e ocupa 4 bytes
Note que i no range é a posição em bytes, não em caracteres. Se precisar de índice por caractere, use []rune:
runes := []rune(texto)
for i, r := range runes {
fmt.Printf("índice_char=%d rune=%c\n", i, r)
}
// Agora os índices são sequenciais: 0, 1, 2, 3, 4, 5, 6
Conversão entre string, []byte e []rune
original := "São Paulo 🇧🇷"
// String → []rune (decodifica UTF-8)
runes := []rune(original)
fmt.Println(len(runes)) // número de caracteres
// []rune → String (codifica UTF-8)
volta := string(runes)
fmt.Println(volta) // "São Paulo 🇧🇷"
// Rune individual → String
s := string('A') // "A"
s = string(9731) // "☃" (boneco de neve)
// int → String (CUIDADO no Go moderno!)
// string(65) cria "A" (rune 65), NÃO "65"
// Para converter número para texto, use strconv:
import "strconv"
s = strconv.Itoa(65) // "65"
O pacote unicode/utf8
O pacote unicode/utf8 é essencial para trabalhar com runes de forma segura:
import "unicode/utf8"
texto := "Programação em Go 🚀"
// Contar caracteres (não bytes)
fmt.Println(utf8.RuneCountInString(texto)) // 19 caracteres
fmt.Println(len(texto)) // 23 bytes
// Decodificar primeira rune
r, tamanho := utf8.DecodeRuneInString(texto)
fmt.Printf("Primeira rune: %c (%d bytes)\n", r, tamanho)
// Decodificar última rune
r, tamanho = utf8.DecodeLastRuneInString(texto)
fmt.Printf("Última rune: %c (%d bytes)\n", r, tamanho)
// Verificar se string é UTF-8 válido
fmt.Println(utf8.ValidString(texto)) // true
fmt.Println(utf8.ValidString("\xff")) // false
// Tamanho de uma rune em bytes
fmt.Println(utf8.RuneLen('A')) // 1
fmt.Println(utf8.RuneLen('é')) // 2
fmt.Println(utf8.RuneLen('🚀')) // 4
O pacote unicode
Para classificação e transformação de runes:
import "unicode"
// Classificação
fmt.Println(unicode.IsLetter('A')) // true
fmt.Println(unicode.IsDigit('9')) // true
fmt.Println(unicode.IsSpace(' ')) // true
fmt.Println(unicode.IsPunct('.')) // true
fmt.Println(unicode.IsUpper('A')) // true
fmt.Println(unicode.IsLower('a')) // true
// Transformação
fmt.Println(unicode.ToUpper('a')) // 'A'
fmt.Println(unicode.ToLower('A')) // 'a'
fmt.Println(unicode.ToTitle('a')) // 'A'
// Verificação de scripts/categorias
fmt.Println(unicode.Is(unicode.Latin, 'A')) // true
fmt.Println(unicode.Is(unicode.Cyrillic, 'Д')) // true
fmt.Println(unicode.Is(unicode.Han, '漢')) // true
Manipulação de texto por caractere
Inverter uma string corretamente
// INCORRETO: inverte bytes, quebra caracteres multi-byte
func inverterErrado(s string) string {
bytes := []byte(s)
for i, j := 0, len(bytes)-1; i < j; i, j = i+1, j-1 {
bytes[i], bytes[j] = bytes[j], bytes[i]
}
return string(bytes) // QUEBRADO para texto com acentos!
}
// CORRETO: inverte runes
func inverter(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(inverter("Olá")) // "álO" (correto)
fmt.Println(inverter("Go 🚀")) // "🚀 oG" (correto)
Truncar texto por caractere
func truncar(s string, maxChars int) string {
runes := []rune(s)
if len(runes) <= maxChars {
return s
}
return string(runes[:maxChars]) + "..."
}
fmt.Println(truncar("Programação em Go", 12)) // "Programação ..."
Contar caracteres específicos
func contarVogais(s string) int {
count := 0
for _, r := range s {
switch unicode.ToLower(r) {
case 'a', 'e', 'i', 'o', 'u', 'á', 'é', 'í', 'ó', 'ú', 'ã', 'õ':
count++
}
}
return count
}
Emojis e caracteres compostos
Emojis modernos podem ser compostos por múltiplos code points Unicode:
// Emoji simples: 1 rune
foguete := "🚀"
fmt.Println(len([]rune(foguete))) // 1
// Emoji composto: múltiplas runes
familia := "👨👩👧👦"
fmt.Println(len([]rune(familia))) // 7 (4 pessoas + 3 ZWJ)
// Bandeira: 2 runes (Regional Indicator Symbols)
brasil := "🇧🇷"
fmt.Println(len([]rune(brasil))) // 2
// Pessoa com tom de pele: 2 runes
pessoa := "👋🏽"
fmt.Println(len([]rune(pessoa))) // 2
Para trabalhar corretamente com emojis compostos e clusters de grafemas, considere usar bibliotecas externas como github.com/rivo/uniseg que implementam a segmentação correta de grafemas Unicode.
RuneError e validação
Quando Go encontra bytes inválidos em UTF-8 durante a decodificação, retorna a rune especial utf8.RuneError (U+FFFD):
import "unicode/utf8"
// Bytes inválidos
dados := []byte{0xff, 0xfe, 0x41} // 2 bytes inválidos + 'A'
s := string(dados)
for i, r := range s {
if r == utf8.RuneError {
fmt.Printf("Byte inválido na posição %d\n", i)
} else {
fmt.Printf("Rune válida: %c na posição %d\n", r, i)
}
}
Sempre valide input de fontes externas com utf8.ValidString() antes de processar, especialmente ao construir APIs REST que recebem texto de usuários.
Runes em contextos práticos
Validação de CPF/CNPJ
func apenasDigitos(s string) string {
var b strings.Builder
for _, r := range s {
if unicode.IsDigit(r) {
b.WriteRune(r)
}
}
return b.String()
}
cpf := apenasDigitos("123.456.789-00") // "12345678900"
Normalização de texto para busca
func normalizar(s string) string {
var b strings.Builder
for _, r := range strings.ToLower(s) {
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == ' ' {
b.WriteRune(r)
}
}
return b.String()
}
Esses padrões são comuns em aplicações backend com PostgreSQL e sistemas de busca.
Boas práticas com runes
- Use
rangepara iterar strings — decodifica runes automaticamente - Use
[]runequando precisar indexar ou fatiar por caractere - Use
utf8.RuneCountInStringpara contar caracteres, nuncalen() - Valide UTF-8 em input externo com
utf8.ValidString() - Cuidado com emojis compostos — podem ser múltiplas runes
- Use
unicode.IsLetterem vez de comparação com ranges ASCII - Prefira
strings.Builder.WriteRunepara construir strings com runes
Para entender a relação entre runes e strings, e como Go representa texto internamente, explore também o glossário de tipos e os tutoriais para iniciantes.
Perguntas frequentes (FAQ)
Qual a diferença entre rune e byte em Go?
byte é um alias para uint8 (1 byte, range 0-255) e representa um único byte. rune é um alias para int32 (4 bytes, range 0-1.114.111) e representa um code point Unicode completo. Caracteres ASCII (inglês básico) cabem em um byte, mas acentos, CJK e emojis precisam de runes. Sempre use runes quando trabalhar com texto Unicode.
Por que Go usa runes em vez de char como outras linguagens?
Go foi projetado por Ken Thompson e Rob Pike (criadores do UTF-8) com suporte Unicode nativo. O nome “rune” vem da tradição Unix e representa claramente um code point Unicode, sem a ambiguidade de “char” que em C/C++ significa apenas 1 byte. Uma rune de 4 bytes pode representar qualquer caractere do Unicode, incluindo emojis e caracteres CJK.
Como contar o número real de caracteres em uma string Go?
Use utf8.RuneCountInString(s) ou len([]rune(s)). Nunca use len(s) que retorna o número de bytes, não de caracteres. Para “café”: len() retorna 5 (bytes), utf8.RuneCountInString() retorna 4 (caracteres). Para emojis compostos como bandeiras, note que um “caractere visual” pode ser múltiplas runes.
É caro converter string para []rune em Go?
Sim, a conversão []rune(s) aloca memória e decodifica toda a string UTF-8. Para strings grandes, evite conversões desnecessárias. Se só precisa iterar, use range (zero alocação extra). Se precisa de posições específicas, considere utf8.DecodeRuneInString para acessar runes individuais sem converter a string inteira.