This library is in flux. APIs may change without notice. Generated from source with koruc 0.1.7 on 11/4/2026.
String
import std/stringstd/string - String library with view/instance ownership model
string.kz · 20 tors· 1 internal
Phantom lifecycles
Derived from the phantom labels in the declarations below — state! issues an
obligation the compiler will chase, !state discharges it, a bare state holds it without moving it. Nothing here is hand-drawn.
String 2 states view!instance!string carries the labels <*@scoped>, <*> with no obligation attached — a dimension, not a lifecycle.
// Creation tors - different allocator sources
//
// Create with explicit allocator
~pub tor new { allocator: std.mem.Allocator, text: string<*@scoped> }
| ok *String<view!>
| err string// Create with page allocator (simple, for demos)
~pub tor from-page { text: string<*@scoped> }
| ok *String<view!>
| err string// Ownership transitions
//
// Take ownership - view becomes instance
~pub tor take { s: *String<!view> } -> *String<instance!>// Release ownership - instance becomes view
~pub tor release { s: *String<!instance> } -> *String<view!>// Cleanup
//
// Free - works on either view or instance (consume-marker on BOTH members, so
// the union discharges either state; `<!view|instance>` marked only `view`,
// hiding `free` from `instance!` obligations — the discharge-finder's
// .state_union branch keys off each member's own consume marker).
~pub tor free { s: *String<!view|!instance> }~pub tor len { s: *String<view|instance> } -> usize// Query / transform operations (FRONTIERS gap 4)
//
// Safe-by-construction: each op returns a scalar or a NEW owned String, never
// a borrowed sub-view of `s`. So the ratified borrow model holds trivially —
// `|` terminal payloads OWN what they carry (a scalar owns itself; substring
// allocates a fresh copy) with zero borrow machinery. Cheap views wait on the
// borrow-obligation surface; `split` (many parts) waits on the store.
//
// Parse the text as a base-10 integer — a read takes `string<*>`, not ownership.
~pub tor parse-int { s: string<*> }
| ok i64
| err string// Build a NEW owned String from a base-10 integer (the int→string mirror of
// parse-int). Owned return, so it carries its own ownership obligation.
~pub tor from-int { n: i64 }
| ok *String<view!>
| err string// Extract [start, end) as a NEW owned String<view!> (a fresh copy, not a
// borrow into `s`). The result carries its own ownership obligation.
~pub tor substring { s: *String<view|instance>, start: usize, end: usize }
| ok *String<view!>
| err string// Split — effect-stream over separated pieces
//
// Split a raw byte-slice on a separator, firing `! piece` per piece DURING the
// call (the read-lines streaming shape, one level down: `! line` is split-on-\n
// of a file; this is split-on-<sep> of a slice). Operates on []const u8 — the
// shape read-lines yields and regex named-groups capture — not a *String handle,
// so it composes directly with line/capture without wrapping. Pieces are
// BORROWED views into `s` (no allocation, no ownership), valid for the body.
//
// This is the naive-phase answer to variable-arity parsing: split into pieces,
// match each whole piece with the anchored `regex:match`. The principled
// alternative — a global/streaming regex (`scan`) that finds repeated matches
// in place — is pinned (640_011) and waits on the engine's unanchored-search
// "later cut"; split needs no engine work.
~pub tor split { s: string<*>, sep: string<*> }
! piece string
| done usize// Write operations - require instance
~pub tor append { s: *String<instance>, text: string<*@scoped> }
| ok
| err string// Single-byte append — TUI key input (todo rename): `k.ch` is a u8, not a
// string literal. Pin 610_022. Without this, every keystroke would need a
// one-char String allocation just to call `append`.
~pub tor append-char { s: *String<instance>, ch: u8 }
| ok
| err string// A String's `data` slice IS its allocation record — there is no capacity
// field, which is why every mutator above reallocs. The two shrinking tors
// below must respect that too: editing `len` behind the allocator's back
// orphans the block, because std.mem.Allocator refuses to act on a
// zero-length slice in BOTH directions —
//
// free(s.data) -> `if (bytes_len == 0) return;` frees nothing
// realloc(s.data, n) -> `if (old_mem.len == 0)` allocates fresh and
// never releases the old pointer
//
// so a shrink-to-zero loses the buffer whether it is disposed or appended
// to next. Pinned as 610_023 (clear) and 610_024 (pop-char). Found by
// kopium's chat pane leaking one draft line per message sent.
//
// Drop the last byte (backspace). Empty stays empty — `| ok` either way so
// a held key doesn't force an err arm.
~pub tor pop-char { s: *String<instance> }~pub tor clear { s: *String<instance> }~part owning koru_std/string.owning.kz
Koru Standard Library: the `owning` part of string.kz. Joined via `~part owning` in string.kz; merged into the module at load. The two ownership hand-offs — lifted for the line wall. `snapshot` copies out (the String keeps living); `detach` moves out (the String ends and the slice's ownership transfers to the caller). Snapshot - the escape path (610_007, ruled 2026-09-05) `read` (`{ s } -> string` via `-> s.data`) retired here: a returned borrow is unpoliceable — the owner can invalidate it by discharge OR mutation — so borrows flow down, never up (KORU095 refuses the projection). Consumers that used `read` to feed a call take the handle instead (borrow-for-the- call, `{{ h:S }}`); genuine escapes copy out through this tor. The copy carries `<view!>` so it cannot leak the way a bare allocating `string` return would. One memcpy, at a boundary, priced as noise.
// Koru Standard Library: the `owning` part of string.kz.
// Joined via `~part owning` in string.kz; merged into the module at load.
//
// The two ownership hand-offs — lifted for the line wall. `snapshot`
// copies out (the String keeps living); `detach` moves out (the String
// ends and the slice's ownership transfers to the caller).
//
// Snapshot - the escape path (610_007, ruled 2026-09-05)
//
// `read` (`{ s } -> string` via `-> s.data`) retired here: a returned borrow
// is unpoliceable — the owner can invalidate it by discharge OR mutation —
// so borrows flow down, never up (KORU095 refuses the projection). Consumers
// that used `read` to feed a call take the handle instead (borrow-for-the-
// call, `{{ h:S }}`); genuine escapes copy out through this tor. The copy
// carries `<view!>` so it cannot leak the way a bare allocating `string`
// return would. One memcpy, at a boundary, priced as noise.
~pub tor snapshot { s: *String<view|instance> }
| ok *String<view!>
| err string// The move-sibling of `snapshot`: consume the String and yield its bytes
// as an owned `string` — no copy, no borrow. `snapshot` copies out and the
// String keeps living; `detach` ends the String and hands the slice's
// ownership to the caller, for custody hand-offs like a Response body
// whose `body_allocated` frees it after send. It returns a bare `string`
// on purpose: the contract is "an owned slice, now yours to dispose" —
// tracked no further because the next holder owns it outright.
~pub tor detach { s: *String<!view|!instance> } -> string~part compare koru_std/string.compare.kz
Koru Standard Library: the `compare` part of string.kz. Joined via `~part compare` in string.kz; merged into the module at load. Predicates — substring containment Does the string contain `needle`?
// Koru Standard Library: the `compare` part of string.kz.
// Joined via `~part compare` in string.kz; merged into the module at load.
//
// Predicates — substring containment
//
// Does the string contain `needle`?
~pub tor contains { s: *String<view|instance>, needle: string }
| yes
| no// First index of `needle`, or not-found.
~pub tor index-of { s: *String<view|instance>, needle: string }
| found usize
| not-found// Total order over raw text: | lt | eq | gt — the std/num:cmp shape.
// Operands are slices (string<*>), not *String handles — a `.data` borrow
// or any string passes directly, same as `split`, so ordering composes
// without wrapping. Byte order; shorter-prefix-first settles equality
// through the shared prefix — the alphabetical tie-break a sort or
// rank-by-key needs. Pure subflow, no allocation, no host call.
//
// A bare `a < b` on strings does NOT lower: the glyph survives Koru's own
// check and Zig refuses `operator < not allowed for type '[]u8'` at
// Stage D — same unserved class as `==` on two string-typed names, which
// needs a type oracle the codegen rewrite site doesn't carry
// (codegen_utils.zig). Whether `<` should route here is Lars's ruling;
// this tor is the namespaced answer meanwhile.
~pub tor cmp { a: string<*>, b: string<*> }
| lt
| eq
| gt~part read koru_std/string.read.kz
Koru Standard Library: the `read` part of string.kz. Joined via `~part read` in string.kz; merged into the module at load. `read` retired as `-> s.data` because a returned borrow is unpoliceable: the owner can still discharge or mutate the buffer the borrow points into (KORU095 — borrows flow down, never up). It returns here in the policeable direction: `! text` lends `s.data` to the arm as an unqualified `string` for the call's duration — `for`, `split`, `parse-int` all take it — with no copy (`snapshot`) and no ownership move (`detach`, whose unlabeled owned slice no Koru leaf can dispose: its contract is a custody hand-off to HOST machinery, `body_allocated`'s post-send free — reaching for it to *read* produced text writes the leak the runtime counter catches). This is the produced-text exit the fmt.blk + `split("")` bridge was standing in for (820_008, gap-detach): `*String -> string` in one hop, `for`-acceptable.
// Koru Standard Library: the `read` part of string.kz.
// Joined via `~part read` in string.kz; merged into the module at load.
//
// `read` retired as `-> s.data` because a returned borrow is unpoliceable:
// the owner can still discharge or mutate the buffer the borrow points
// into (KORU095 — borrows flow down, never up). It returns here in the
// policeable direction: `! text` lends `s.data` to the arm as an
// unqualified `string` for the call's duration — `for`, `split`,
// `parse-int` all take it — with no copy (`snapshot`) and no ownership
// move (`detach`, whose unlabeled owned slice no Koru leaf can dispose:
// its contract is a custody hand-off to HOST machinery, `body_allocated`'s
// post-send free — reaching for it to *read* produced text writes the
// leak the runtime counter catches). This is the produced-text exit the
// fmt.blk + `split("")` bridge was standing in for (820_008, gap-detach):
// `*String -> string` in one hop, `for`-acceptable.
~pub tor read { s: *String<view|instance> }
! text string
| done usize