Strings and bytes
What you'll learn:
- explain why
len("привет")returns12, not6; - tell a byte from a rune and walk through a string character by character;
- build a new string from parts, since the old one cannot change;
- search, split and join strings with the functions of the
stringspackage.

Picture a row of letter tiles, like the tiles of a word board game. Each tile is one byte: a small number from 0 to 255, and the letter on it is only a way to show that number. A string in Go is built like that row: bytes that hold the text. A Latin letter, a digit or a space takes one tile, and a Cyrillic letter takes two.
This picture leads to two surprises. You can index a string in Go like a slice: s[0] is its first element. But the result looks unexpected:
- the first element of the string
"Go"prints as a number; - the length of
"Привет"is twice its number of letters.
package main
import "fmt"
func main() {
s := "Go"
fmt.Println(len(s), s[0]) // 2 71
fmt.Println(len("Привет")) // 12
}Let's walk through the output:
len(s)returns2:"Go"has two Latin letters, one tile each.s[0]returns71, the number on the first tile.len("Привет")returns12: the word has six letters of two tiles each.
A string of bytes
A string in Go holds a sequence of bytes that store the text in the UTF-8 encoding. An encoding is the rule that turns each letter into bytes. In UTF-8, a Latin letter, a digit or a space takes one byte, and a Cyrillic letter takes two.
The len function returns the number of bytes, not letters: it counts tiles. That is why the string "Привет" has six letters and twelve bytes.
Interviewers like to ask: "What does len("привет") return?" The right answer is 12, because len on a string always counts bytes.
An index returns a byte
The index s[i] returns one byte, a value of type byte. Here is what follows from that:
- the type
byteis another name foruint8, an integer from0to255; - so
fmt.Printlnprints not the letterGbut its code71; - you compare a byte with a letter using single quotes:
s[0] == 'G'givestrue.
Press the button to read "Go Привет" byte by byte, as s[i] does, and watch how many bytes a Latin letter, a space and a Cyrillic letter take.
A string holds UTF-8 bytes. Press the button to read it one byte at a time, the way s[i] does.
The index bounds are the same as for a slice:
- indexes run from
0tolen(s) - 1; - an index past the end causes a panic;
- the empty string
""has length0, and any index into it panics.
A string cannot change
Strings in Go are immutable: the bytes of a created string never change. Here the tile picture stops working: you can take a real tile out and replace it, but not a byte of a string.
What you can and cannot do:
- you cannot assign a new value to the element
s[i]; - you can assign a whole different string to the variable;
- a new string is built with the
+operator from parts of the old one.
A new string from parts
s := "Go"
t := "g" + s[1:]
fmt.Println(t, s) // go GoLet's walk through the example line by line:
s := "Go"creates a string of two bytes.t := "g" + s[1:]builds a new string. The expressions[1:]takes a part of the string by the same rules as slicing a slice, and returns a string. The bytes from index1to the end go intottogether with the letter"g".fmt.Println(t, s)printsgo Go. The stringsitself did not change: the+operator creates a new string from the bytes of both parts.
The bounds in s[1:] are counted in bytes too, not in letters. For "Go" that is the same thing. But "Привет"[1:] starts in the middle of the letter П: such a slice cuts the letter in half, and printing shows a garbled sign in its place.
Copying and comparing
Two operations work on a string as a whole:
- the assignment
t := sdoes not copy bytes: both variables see the same text, and neither can spoil it; - the
==operator compares two strings: it compares their bytes in order.
Common mistake
The first letter of a string is replaced by assigning through an index:
s := "hello"
s[0] = 'H' // ✗cannot assign to s[0] (neither addressable nor a map index expression)The compiler rejects the assignment because the string is immutable, and the program does not build. Build a new string from parts instead: s = "H" + s[1:].
Key points
- A string is a row of bytes in UTF-8;
lencounts bytes, solen("привет")is12. s[i]returns one byte of typebyte: a number from0to255, not a letter.- Slicing a string, as in
s[1:], also cuts by bytes and can split a letter in half. - A string cannot change:
s[0] = 'H'does not compile, and a new string is built from parts with+.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.