Understanding String Slicing in Go

Slicing, a common operation in many programming languages, refers to accessing a section of a string using its indices. In Go, strings are UTF-8 encoded and immutable, meaning that any operation, including slicing, creates a new string without modifying the original one.

Basic Syntax of String Slicing

The most common syntax for slicing strings in Go is using the colon : operator within brackets, like str[start:end]. This returns a new substring, beginning at index start and extending up to, but not including, index end.


package main

import (
    "fmt"
)

func main() {
    str := "Hello, Gopher!"
    slice1 := str[0:5] // Retrieves "Hello"
    slice2 := str[7:13] // Retrieves "Gopher"

    fmt.Println(slice1) // Output: Hello
    fmt.Println(slice2) // Output: Gopher
}

Omitting Start or End Index

When slicing, you can omit either the start or end index to default to the beginning or end of the string, respectively.


package main

import (
    "fmt"
)

func main() {
    str := "Programming in Go"
    fmt.Println(str[:11]) // Outputs: Programming
    fmt.Println(str[12:]) // Outputs: in Go
}

Understanding Indices and Runes

Because Go strings are UTF-8 encoded, slicing by index may not work as expected for strings that contain multi-byte characters, such as emojis or accented letters. For example, attempting to slice based on individual characters might lead to unexpected results.


package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    str := "Café ☕"
    iterIndexUtf8(str)
}

func iterIndexUtf8(s string) {
    for len(s) > 0 {
        r, size := utf8.DecodeRuneInString(s)
        fmt.Printf("%c ", r)
        s = s[size:]
    }
}

The above code correctly prints "Café ☕" iterating over each rune, demonstrating how entirely slicing could misinterpret characters unless handled properly.

Practical Examples of String Slicing

Considering real-world scenarios where string slicing can simplify tasks:


package main

import (
    "fmt"
    "strings"
)

func main() {
    filename := "document.pdf"

    // Strip the extension
    dot := strings.LastIndex(filename, ".")
    name := filename[:dot]
    fmt.Println(name) // Output: document

    // Get the extension
    ext := filename[dot+1:]
    fmt.Println(ext) // Output: pdf
}