← Go, End to End

Go, End to End

Strings, Runes, and Bytes

A Go string is an immutable sequence of bytes, conventionally (but not enforced) holding UTF-8 encoded text. Once created, a string's contents can never be modified in place, any "modification" actually builds a new string.

byte vs rune

A byte is an alias for uint8, a single 8-bit value. A rune is an alias for int32 and represents a single Unicode code point, which can take anywhere from 1 to 4 bytes to encode in UTF-8. Confusing the two is the source of almost every "why is my string indexing broken" question from newcomers.

go · live, editable, runnable

Indexing gives you bytes, range gives you runes

s[i] accesses the i-th byte of a string, which may be in the middle of a multi-byte character. Ranging over a string with for i, r := range s instead decodes UTF-8 properly and gives you full runes, with i as the byte offset where that rune starts (which is why the indices can skip numbers for multi-byte characters).

go · live, editable, runnable

Notice the byte offsets jump from 1 to 3, skipping 2, because 'é' takes 2 bytes. If you'd instead looped with a plain index (for i := 0; i < len(s); i++) and printed s[i], you'd get raw bytes, and printing one half of a multi-byte character as if it were a whole one produces garbage.

Building strings efficiently

Since strings are immutable, repeatedly concatenating with + in a loop reallocates a new string every time, which is quadratic in the number of concatenations for a loop of that size. strings.Builder avoids this by writing into a growable internal buffer and only producing the final string once, at the end.

go · live, editable, runnable

A quick tour: strings, strconv, unicode

  • strings: searching, splitting, joining, replacing, case conversion (strings.Contains, strings.Split, strings.Join, strings.ToUpper, and many more)
  • strconv: converting between strings and other types (strconv.Atoi, strconv.Itoa, strconv.ParseFloat, strconv.FormatBool)
  • unicode: classifying individual runes (unicode.IsDigit, unicode.IsSpace, unicode.IsUpper)
go · live, editable, runnable

Try it yourself: write a function that reverses a string correctly for multi-byte characters (converting to []rune first, reversing that, then converting back), and confirm it handles "héllo" without corrupting the é.