proc main(): void {
s := "café au lait"
echo len(s) // 12 characters
echo bytes(s) // 13 bytes — é takes two
if s bounds 3 {
echo s[3] // é, the fourth character
echo s[0:3] // café — both bounds inclusive
}
// A position from indexOf is one the string really has, so the
// slice taken at it needs no guard of its own.
if indexOf(s, "au") is Result.Ok(at) {
echo s[at:] // au lait
}
for each c in split("bee", "") {
echo c
}
}A Str is a sequence of characters, and everything addresses it as one. len counts characters where bytes counts the UTF-8 they take, so for "café" they are 4 and 5. s[0] is the first character, as a one-character Str, and s[1:3] is characters one to three, both bounds inclusive.
Those are proved in range exactly as a vector's are, with one difference: a string never has a length the compiler knows, because there is no Str[3] for text. So even s[1] on a literal sitting right there needs a guard, or a position indexOf answered with.
Nothing writes into a Str: s[0] = "x" is a compile error, because characters are not all the same width and writing one would move every character after it. Build the new string instead. for each does not walk a Str either — split(s, "") is its characters, as a vector.