What is the rune data type in Go?

Explore the rune data type in Go, an alias for int32 used to represent Unicode code points. Learn how runes enable handling non-ASCII characters and strings from different languages. Discover how runes connect to UTF-8 encoding and leverage the utf8 package for encoding/decoding operations. Master character-level string processing with runes through code examples covering case checks, digit validation, and more. Understand why runes are vital for building robust, internationalized Go applications supporting diverse languages and character sets.

Go
rune
UTF-8
Unicode
Published: 03/27/2024|By: Tole Ephen

Introduction

In the Go programming language, the rune is a data type that represents a Unicode code point, serving as an alias for the int32 data type. It is particularly useful when working with strings containing non-ASCII characters or characters from different languages. Go's string data type is built upon UTF-8 encoded runes, where a string is essentially a read-only slice of bytes, and each byte represents a single rune (Unicode code point). The rune data type is vital for handling Unicode characters and is closely tied to UTF-8 encoding, which is used for representing Unicode in Go strings. The utf8 package provides functions to convert between runes and their UTF-8 byte representations. Working with runes enables character-level operations like case checks and digit validation, essential for text processing. By understanding runes and UTF-8, Go developers can build robust, internationalized applications that support diverse languages and character sets.

Declaring and using runes:

package main

import "fmt"

func main() {
    // Declare a rune variable
    var r rune = 'a'
    fmt.Printf("Type of r: %T\n", r)
	// Output: Type of r: int32
}

Next, let's convert a string to a slice of runes and print each rune:

// Convert string to a slice of runes
name := []rune("Gophergram")

// Print each rune in the string
for _, r := range name {
	fmt.Printf("%c ", r)
}
// Output: G o p h e r g r a m

Working with individual characters using runes

Runes are particularly useful when you need to perform operations on individual characters within a string. For example, you can use runes to check if a character is uppercase, lowercase, or a digit:

Code example: Character checks with runes

package main

import (
    "fmt"
    "unicode"
)

func main() {
    r := 'Σ' // Greek letter Sigma

    // Check if the rune is uppercase
    if unicode.IsUpper(r) {
        fmt.Printf("%c is uppercase\n", r)
    }

    // Check if the rune is lowercase
    if unicode.IsLower(r) {
        fmt.Printf("%c is lowercase\n", r)
    }

    // Check if the rune is a digit
    if unicode.IsDigit(r) {
        fmt.Printf("%c is a digit\n", r)
    }

    // Output: Σ is uppercase
}

Working with UTF-8 Encoding

Runes are integral to working with Unicode strings in Go, and understanding their usage is essential for building robust and internationalized applications. However, runes are closely related to UTF-8 encoding, which is the encoding used for representing Unicode code points in Go strings.

The utf8 package

Go provides the utf8 package for working with UTF-8 encoded strings and runes. This package offers various functions to convert between runes and their UTF-8 encoded byte representations.

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    // Declare a string
    name := "Gophergram"

    // Get the number of runes (Unicode code points) in the string
    runeCount := utf8.RuneCountInString(name)
    fmt.Printf("Number of runes in '%s': %d\n", name, runeCount)

    // Iterate over the runes in the string
    for i, r := range []rune(name) {
        fmt.Printf("%d: %c\n", i, r)
    }

    // Output:
    // Number of runes in 'Gophergram': 10
	// 0: G
	// 1: o
	// 2: p
	// 3: h
	// 4: e
	// 5: r
	// 6: g
	// 7: r
	// 8: a
	// 9: m

    // Encode a rune slice as a UTF-8 byte slice
	var bytes []byte
	runes := []rune{'H', 'e', 'l', 'l', 'o', ' ', '🌎'}
	for _, r := range runes {
		bytes = utf8.AppendRune(bytes, r)
	}
	fmt.Printf("%s\n", bytes)

    // Output:
    // Hello 🌎

	// Decode the first UTF-8 encoding in bytes
	decodedRunes, size := utf8.DecodeRune(bytes)
	fmt.Printf("Decoded runes: %c (%d)\n", decodedRunes, size)

    // Output:
    // Decoded runes: H
}

Understanding the output

In this example, we first count the number of runes (Unicode code points) in a string using utf8.RuneCountInString. This is useful because a single rune can be represented by one or more bytes in UTF-8 encoding, depending on the code point value.

Next, we iterate over the runes in the string by converting the string to a slice of runes []rune(name).

We then create a slice of runes containing the characters "Hello 🌎" and encode it into a byte slice using utf8.AppendRune. This byte slice represents the UTF-8 encoded representation of the rune slice.

Conversely, the utf8.DecodeRune function decodes the first UTF-8 encoding. In the example, we decode the first rune from the encoded byte slice, which is the character 'H'.

The connection between runes and UTF-8 encoding is that runes represent Unicode code points, while UTF-8 is a variable-width encoding used to represent these code points as a sequence of bytes. Go strings are stored as UTF-8 encoded byte slices, and the utf8 package provides functions to convert between runes and their UTF-8 encoded byte representations.

Happy Coding :)