Rune Literals

Understand rune literals in Go - single-quoted Unicode code point constants, supported escape sequences, and how to avoid the most common mistakes when working with individual characters.

A rune literal in Go is a single Unicode code point enclosed in single quotes, like 'a' or '\n'. It represents a constant of type rune, which is an alias for int32. The value is the integer code point itself — 'a' is literally 97 and '世' is 19990. That integer is four bytes wide even when the encoded character is smaller, because rune is always int32.

Rune vs byte:

A rune holds a code point, not raw UTF-8 bytes. The character 'ä' (U+00E4) is a single rune, but its UTF-8 encoding is two bytes: 0xc3 0xa4. A rune is always 4 bytes; a byte is 1. Don't confuse the two.

Why Rune Literals Exist

Before Unicode, character sets like ASCII used one byte per character and covered only English letters, digits, and punctuation. Every language beyond English needed its own encoding, which made handling international text painful and error-prone. Unicode solved this by assigning a unique number — a code point — to every character across all writing systems.

Go was designed to treat text as Unicode from the ground up. A rune literal gives you a way to write a single code point directly in source code. This matters because real-world programs constantly need to test for specific characters (like '\n' for newline), build parsers, or validate input. Without rune literals, you would have to remember numeric code points or use awkward escape sequences everywhere.

How Rune Literals Work

A rune literal is an integer constant. When you write 'A', the compiler stores the value 65 (the Unicode code point U+0041) as a 32-bit integer. You can use it anywhere an int32 is expected.

package main
import "fmt"
func main() {
    r := '€'
    fmt.Printf("Value: %d, Unicode: %U, Character: %c, Type: %T\n", r, r, r, r)
}

This prints: Value: 8364, Unicode: U+20AC, Character: €, Type: int32. The format verb %c prints the character, %U shows the Unicode notation, and %d reveals the integer underneath. The variable r is truly an integer — you can add or compare it like any other number.

A reader encountering rune literals for the first time can think of them as "named numbers for characters." Instead of writing 0x20AC to refer to the euro sign, you write '€'. The compiler does the lookup for you, and the source code remains readable.

Basic Syntax

A rune literal is one or more characters wrapped in single quotes. The simplest form is a visible character, like 'x'. Since Go source files are UTF-8 encoded, you can write any Unicode character directly inside those single quotes.

'a'   // Latin small letter a
'ä'   // Latin small letter a with diaeresis
'本'  // CJK unified ideograph
'😊' // Emoji (U+1F60A)

What appears as a single visual character may be stored in multiple UTF-8 bytes in the source file, but it must represent exactly one Unicode code point. The compiler accepts the literal only if the content between the quotes decodes to a single code point. This is the fundamental rule: one literal, one code point.

Multiple code points cause a compilation error:

If you place two code points inside single quotes — even if they visually combine to look like a single character — the compiler rejects the program. For example, 'ў' (Cyrillic у followed by combining breve) is illegal because it contains two code points. The error message is: more than one character in rune literal.

Escape Sequences

Not every character is easy to type or safe to embed directly. A newline inside single quotes would break the syntax, and control characters like backspace aren't visible. Escape sequences solve this by letting you write the code point value using ASCII characters only. All escapes start with a backslash \.

Special Character Escapes

A handful of one-letter escapes represent common control characters:

EscapeMeaningCode point
\aAlert (bell)U+0007
\bBackspaceU+0008
\fForm feedU+000C
\nNewlineU+000A
\rCarriage returnU+000D
\tHorizontal tabU+0009
\vVertical tabU+000B
\\BackslashU+005C
\'Single quoteU+0027

These work exactly as they do in many other languages. For example, '\n' is a rune literal representing the newline character, and '\\' is a literal backslash.

Octal Escapes

A backslash followed by exactly three octal digits (0-7) encodes a code point in the range 0–255. The value must fit in a byte.

'\101'  // 'A' (octal 101 = 65)
'\000'  // null character
'\377'  // 255 in decimal

Octal escapes are limited to 0-255:

'\400' is illegal because 400 octal is 256, which exceeds the maximum byte value. Use hex or Unicode escapes for larger code points.

Hex Escapes

\x followed by exactly two hexadecimal digits encodes a single byte. Again, the range is 0–255.

'\x41'  // 'A'
'\x0a'  // newline
'\xff'  // 255

Unicode Escapes: \u and \U

For code points beyond 255, Go provides two Unicode escape forms:

  • \u followed by exactly four hexadecimal digits (16-bit code point)
  • \U followed by exactly eight hexadecimal digits (32-bit code point)
'\u0041'     // 'A'
'\u00e4'     // 'ä'
'\u03c0'     // 'π'
'\U0001F60A' // '😊'

The \U form is necessary for code points above U+FFFF, such as many emoji. Both forms must represent a valid Unicode code point (≤ U+10FFFF) and cannot encode surrogate halves (U+D800–U+DFFF). Attempting to write '\uDFFF' or '\U00110000' causes a compilation error.

Invalid code points are rejected at compile time:

Surrogate halves and code points above U+10FFFF are not allowed. The compiler will report an error immediately.

You can mix escapes and literal characters in a single rune literal only if the whole thing resolves to a single code point. For instance, 'a' and '\x61' are the same literal. Combining a literal with an escape that forms a separate code point is not allowed — that would be two code points.

Common Mistakes and Misunderstandings

Using single quotes where double quotes are needed

The most frequent beginner error is accidentally using single quotes for a string, especially in functions like fmt.Printf or fmt.Scanf. In Go, single quotes are exclusively for rune literals. Writing fmt.Printf('%d', n) triggers a compile error because '%d' is a rune literal with more than one character.

Single quotes are not string delimiters:

If you see an error like more than one character in rune literal, check for a format string accidentally wrapped in single quotes instead of double quotes. Replace '%d' with "%d".

Assuming a rune is a visible character

A rune is a number. Some rune values represent control characters that have no visual form, like '\x00' (null). Printing them with %c may produce no visible output or alter terminal behavior. Always use %U or %+q when debugging to see the actual code point.

Combining characters cannot be in a single rune literal

Unicode allows characters to be formed by a base letter followed by combining marks (accents, diacritics). For example, ü can be the precomposed character U+00FC ('ü') or the sequence u (U+0075) plus combining diaeresis (U+0308). The first is a single code point and is a valid rune literal. The second is two code points and must go in a string, not a rune literal.

// Valid: precomposed character (one code point)
precomposed := 'ü'
// Invalid: would be two code points
// combined := 'ü'   // compiler error

If you copy a character from a source that uses combining sequences, the literal may fail even though it looks identical on screen. When you hit a more than one character in rune literal error and the character looks like a single glyph, look for combining marks.

Forgetting that indexing a string gives bytes, not runes

This is not about rune literals themselves, but it is the most common downstream confusion. A rune literal holds a code point; a string is a sequence of bytes. Indexing a string yields the raw byte at that position, not the rune.

s := "€"
fmt.Printf("%d\n", s[0]) // 226, first byte of UTF-8 encoding of €
r := '€'
fmt.Printf("%d\n", r)    // 8364, the actual code point

The rune literal '€' is the number 8364. The first byte of the string "€" is 226. They are completely different numbers. Beginners frequently expect s[0] to give them the code point, because in some other languages strings are arrays of characters. In Go they are arrays of bytes, and rune literals help you reason about the difference.

Using rune literals for comparison is safe:

A direct comparison between a rune literal and a rune value extracted from a string works correctly because both are code points. For example, r == '€' compares two integers — no encoding confusion. Use for _, r := range s to iterate over code points safely.

Practical Usage

Rune literals appear wherever you need to test or produce individual characters. In a parser, you might check for a specific delimiter:

func isDigit(r rune) bool {
    return r >= '0' && r <= '9'
}

In a state machine, you compare rune values directly:

switch ch {
case '(', '{', '[':
    // opening bracket
case ')', '}', ']':
    // closing bracket
}

They are also used to initialize constants for special characters:

const (
    Bell    = '\a'
    Newline = '\n'
    Tab     = '\t'
)

When you need to represent a code point that you cannot or prefer not to type, escape sequences keep the source code ASCII-clean while still representing the exact code point:

deg := '\u00B0'  // degree symbol °
fmt.Printf("Temperature: 23%cC\n", deg)

No dynamic allocation occurs — a rune literal is a compile-time constant. It carries no overhead at runtime beyond storing an int32.