String Performance in Reef

Reef strings are immutable: every concatenation allocates a new string and copies all characters from both operands. This is fine for one-off operations, but becomes a performance trap when done repeatedly in a loop.

The O(n²) Concatenation Trap

Consider building a large string by appending lines in a loop:

// BAD — O(n²) time and memory
mut result: string = ""
mut i: int = 0
while i < line_count
    result = result + lines[i] + "\n"   // copies ALL of result every iteration
    i = i + 1
end while

Each iteration copies the entire accumulated string plus the new line into a fresh allocation. For n lines of average length k, total work is proportional to n² * k, so building a very large string one piece at a time quickly gets slow. Use one of the approaches below instead.

str.join() — Best for combining an array of strings

import core.str

let result = str.join(lines, "\n")

join() pre-calculates the total length, makes one allocation, and copies each part exactly once — O(n) time and space.

Use str.join() when you already have all parts in an array (or can collect them first).

text.stringbuilder — Best for incremental / conditional building

import text.stringbuilder as sb

let builder = sb.sb_new()
mut i: int = 0
while i < line_count
    if should_include(lines[i])
        sb.sb_append(builder, lines[i])
        sb.sb_append(builder, "\n")
    end if
    i = i + 1
end while
let result = sb.sb_build(builder)

StringBuilder collects parts and joins them in a single pass at the end. Use it when you need to build a string incrementally with conditional logic.

io.buffer — Best for writing large output to a file

import io.buffer
import core.result

let w = result.unwrap_ok(buffer.open_buffered_writer("/path/to/output.txt"))
mut i: int = 0
while i < line_count
    buffer.writer_write_line(w, lines[i])
    i = i + 1
end while
buffer.writer_close(w)

(open_buffered_writer returns Result[BufferedWriter, Error] — unwrap it to get the handle used below; see 035_ERROR_HANDLING.md for the general pattern.)

BufferedWriter maintains an 8 KB internal buffer and flushes to disk automatically. If your goal is to write to a file (not build an in-memory string), this avoids allocating the full result entirely.

io.stream — Low-level streaming writes

import io.stream
import core.result

let s = result.unwrap_ok(stream.stream_open_write("/path/to/output.txt"))
mut i: int = 0
while i < line_count
    stream.stream_write(s, lines[i])
    stream.stream_write(s, "\n")
    i = i + 1
end while
stream.stream_close(s)

(stream_open_write also returns a Result — same unwrap pattern as above.)

io.stream writes directly to the file descriptor without buffering. Prefer io.buffer for many small writes; use io.stream when you are already writing large chunks.

Quick Reference

Scenario API Module
Join an array of strings str.join() core.str
Build a string with conditional logic sb_new / sb_append / sb_build text.stringbuilder
Write many lines to a file open_buffered_writer io.buffer
Stream large chunks to a file stream_open_write io.stream
Concatenate 2–3 strings once concat() or + core.str

Rule of thumb: If you see result = result + something inside a loop, replace it with one of the APIs above.