Chapter 5

Strings

A string is a length and some bytes on the heap. The standard library gives you seventeen ways to work with them, and every one produces a new string, because in Sure, strings do not change — they are replaced.

Basics

+ concatenates (and allocates). == and != compare contents, not identity. len counts bytes. Escapes cover the usual suspects: \n, \t, \r, \", \\, \0.

func main() -> Int {
    let greeting = "hello" + ", " + "world"
    print(greeting == "hello, world")   // true
    print(len(greeting))                // 12
    return 0
}

Taking strings apart

find returns the index of a substring or -1. slice cuts by byte range, start inclusive, end exclusive, clamped to the string — slicing cannot fail, only disappoint. split divides on a separator into a [String].

func main() -> Int {
    let request = "GET /index.html HTTP/1.1"
    let first_space = find(request, " ")
    let rest = slice(request, first_space + 1, len(request))
    let path = slice(rest, 0, find(rest, " "))
    print(path)                          // /index.html

    let parts = split(request, " ")
    print(parts[0])                      // GET
    print(len(parts))                    // 3
    return 0
}

That is real protocol parsing, and it is the exact code the web server in chapter 10 uses. join is split's inverse: join(parts, " ") reassembles.

Questions and transformations

CallAnswers
contains(s, sub)is sub in there?
starts_with(s, p), ends_with(s, p)the edges
trim(s)a copy without surrounding whitespace
upper(s), lower(s)ASCII case, changed
replace(s, old, new)every occurrence, replaced
byte_at(s, i)one byte, as an Int (bounds-checked)
chr(n)one byte, as a String
func main() -> Int {
    let shout = upper(trim("  make it loud  "))
    print(shout + "!")                       // MAKE IT LOUD!
    print(replace("a-b-c", "-", "::"))       // a::b::c
    print(byte_at("A", 0))                   // 65
    print(chr(66))                           // B
    return 0
}

To numbers and back

str(x) renders an Int, Float, or Bool. to_int(s) and to_float(s) parse; a string that isn't a number parses to zero, calmly. Validate first if zero would flatter your input.

func main() -> Int {
    let port = to_int("8080")
    print(port + 1)              // 8081
    print(to_float("2.5"))       // 2.5
    print(to_int("sure"))        // 0
    return 0
}
Bytes, not code points. Sure strings are byte strings. len, slice, and byte_at count bytes; UTF-8 text passes through everything intact, but a slice boundary in the middle of a multi-byte character is your slice boundary, faithfully executed. The library does what you say, which is the most and the least it can do.