Runes in Golang

If you prepare for Go interviews, you must understand rune.

Many string problems look easy at first, but they break when the input contains Unicode characters such as a, Z, 你, 界, or 🙂.

That is where rune becomes important.

What is a rune in Go?

In Go, a rune is an alias for int32.

It represents a Unicode code point.

That means a rune can store values such as:

  • 'A'
  • 'z'
  • 'ä½ '
  • '界'

Example:

package main

import "fmt"

func main() {
	var ch rune = '界'
	fmt.Printf("%c %T %d\n", ch, ch, ch)
}

Output:

界 int32 30028

Here:

  • %c prints the character
  • %T prints the type
  • %d prints the Unicode code point as an integer

Why interviewers ask about runes

Interviewers ask about rune because many candidates assume that:

  • one string index means one character
  • len(s) means number of characters
  • reversing a string by bytes is always safe

All of these assumptions can be wrong in Go.

If the string contains only plain ASCII characters, byte-based code may work.

If the string contains multi-byte Unicode characters, the same code may fail.

Difference between byte and rune

This is the most important distinction:

  • byte is an alias for uint8
  • rune is an alias for int32
  • byte usually represents raw data or one UTF-8 byte
  • rune represents one Unicode code point

Example:

package main

import "fmt"

func main() {
	s := "Go语言"

	fmt.Println("len(s):", len(s))
	fmt.Println("len([]rune(s)):", len([]rune(s)))
}

Output:

len(s): 8
len([]rune(s)): 4

Why?

  • G takes 1 byte
  • o takes 1 byte
  • 语 takes 3 bytes in UTF-8
  • 言 takes 3 bytes in UTF-8

So the string has:

  • 8 bytes
  • 4 runes

Important fact about strings in Go

A Go string is a read-only sequence of bytes.

So:

  • len(s) returns number of bytes
  • s[i] returns a byte, not a rune

Example:

package main

import "fmt"

func main() {
	s := "界"

	fmt.Println(len(s))
	fmt.Println(s[0])
}

Output:

3
231

The output is 3 because 界 takes 3 bytes in UTF-8.

The value 231 is only the first byte of that character, not the full character.

This is the reason many interview solutions fail for Unicode input.

How to iterate over runes correctly

Use a for range loop when you want to process characters safely.

Example:

package main

import "fmt"

func main() {
	s := "Go界"

	for i, r := range s {
		fmt.Printf("byte index: %d, rune: %c, code point: %d\n", i, r, r)
	}
}

Output:

byte index: 0, rune: G, code point: 71
byte index: 1, rune: o, code point: 111
byte index: 2, rune: 界, code point: 30028

Notice that:

  • i is the byte index
  • r is the rune value

This is a very common interview detail.

Many candidates think i is the character position, but it is actually the byte position in the original string.

How to index by character position

If you want to access the 3rd character, convert the string to []rune first.

Example:

package main

import "fmt"

func main() {
	s := "Go语言"
	runes := []rune(s)

	fmt.Printf("%c\n", runes[2])
}

Output:

语

This is the correct approach when an interview problem says:

  • get the kth character
  • reverse the string
  • compare characters from left and right
  • build a frequency map by character

Converting between string and rune slice

You will often use these two conversions:

runes := []rune(s)
s2 := string(runes)

This is useful when you need to modify characters, because strings in Go are immutable.

Example:

package main

import "fmt"

func main() {
	s := "cat"
	runes := []rune(s)

	runes[0] = 'b'

	fmt.Println(string(runes))
}

Output:

bat

Rune literals

Single quotes represent rune literals in Go.

Examples:

var a rune = 'A'
var b rune = 'ä½ '
var newline rune = '\n'

Do not confuse:

  • 'A' -> rune
  • "A" -> string

Common interview problems where runes matter

Runes are especially important in problems involving:

  • string reversal
  • palindrome checking
  • frequency counting
  • sliding window over characters
  • anagram detection
  • first non-repeating character
  • string compression or transformation

If the interviewer says the input can contain Unicode, you should immediately think about rune.

Example 1: Reverse a string correctly

This is one of the most common interview questions.

Wrong approach:

package main

func reverseBytes(s string) string {
	bytes := []byte(s)
	left, right := 0, len(bytes)-1

	for left < right {
		bytes[left], bytes[right] = bytes[right], bytes[left]
		left++
		right--
	}

	return string(bytes)
}

This works for ASCII, but may break for Unicode strings.

Correct approach:

package main

func reverseRunes(s string) string {
	runes := []rune(s)
	left, right := 0, len(runes)-1

	for left < right {
		runes[left], runes[right] = runes[right], runes[left]
		left++
		right--
	}

	return string(runes)
}

Why this works:

  • each element in runes is one Unicode code point
  • swapping happens character by character, not byte by byte

Example 2: Palindrome check with Unicode support

package main

func isPalindrome(s string) bool {
	runes := []rune(s)
	left, right := 0, len(runes)-1

	for left < right {
		if runes[left] != runes[right] {
			return false
		}
		left++
		right--
	}

	return true
}

This is the preferred pattern when the problem is defined in terms of characters.

Example 3: Character frequency map

If the task is to count characters, use map[rune]int.

package main

import "fmt"

func main() {
	s := "a界a界b"
	freq := make(map[rune]int)

	for _, r := range s {
		freq[r]++
	}

	fmt.Println(freq['a'])
	fmt.Println(freq['界'])
}

This is better than map[byte]int when Unicode input is possible.

A subtle but important point

In most interview settings, people casually say “character”.

In real Unicode processing, one visible symbol is not always equal to one rune.

For example, some emoji and accented characters can be formed by multiple code points.

For most Go interview problems, using rune is the correct and expected solution.

But if the problem becomes fully Unicode-display-aware, then even []rune may not be enough.

That is an advanced topic. For standard interviews, remember:

  • bytes are for raw UTF-8 data
  • runes are for Unicode code points
  • []rune is usually the right tool for character-based logic

Common mistakes

Here are the mistakes interviewers often expect you to avoid:

  1. Using len(s) as character count
  2. Using s[i] and thinking it is a full character
  3. Reversing []byte for Unicode strings
  4. Using map[byte]int for character frequency when the input may be Unicode
  5. Forgetting that range returns byte index, not rune position

Interview checklist

When you see a string problem in Go, ask yourself:

  1. Is the input ASCII only, or can it contain Unicode?
  2. Do I need bytes, or do I need characters?
  3. Should I iterate with range?
  4. Do I need to convert to []rune for indexing or swapping?
  5. Should my frequency map use rune as the key?

If the problem is character-based, this pattern is usually safe:

runes := []rune(s)

Then solve the problem on runes.

Summary

To master string interview problems in Go, remember these rules:

  • a Go string stores bytes
  • len(s) gives bytes, not characters
  • s[i] gives a byte
  • rune represents a Unicode code point
  • for range reads runes from a string
  • []rune(s) is the correct choice when you need character-based indexing or updates

If you understand these points well, you will avoid many bugs and write interview-ready Go solutions for string problems.