std::string
String Module
Owned, heap-backed UTF-8 byte string type and related utilities.
Overview
String is a value-type record whose data buffer lives on the heap.
It implements Drop so the buffer is freed automatically when the
owning binding goes out of scope, and Clone for explicit deep copies.
Its storage is an owned UTF-8 byte buffer with a byte-counted len field.
str is the primitive immutable string-slice type (const char* in C).
In v0.4 it remains a borrowed NUL-terminated UTF-8 byte view.
String literals have type str. Use String::create(s) to create an
owned copy from a str.
Interior NUL bytes are preserved in owned storage. toStr() is a zero-copy interop view. C-string consumers may stop at the first interior NUL.
Memory Layout
String
┌──────────────┐
│ data ────────┼──────► ┌───┬───┬───┬───┬───┬───┬───┬───┐
│ len: 5 │ │ h │ e │ l │ l │ o │ │ │ │
│ cap: 8 │ └───┴───┴───┴───┴───┴───┴───┴───┘
└──────────────┘ ◄── used bytes ──►◄─ reserved ──►
Fields mirror the C runtime’s IgnisString layout exactly:
data— pointer to the backing byte buffer (ornullbefore init)len— number of bytes currently storedcap— total bytes allocated
UTF-8 Scanning and Bytes
String stays byte-backed. APIs that need parser or diagnostic offsets keep
those byte positions internal to the caller while decoding char values at
explicit byte boundaries.
charAt and pushChar work with decoded char values, while byteAt,
pushByte, forEachByte, findByte, trimWhere, and split keep raw byte
behavior explicit.
Higher-Order Iteration Methods
String provides callback-based operations for char/byte iteration, searching, trimming, and splitting:
| Method | Callback signature | Returns | Description |
|---|---|---|---|
forEach |
(char) -> void |
void |
Iterate scalar char values |
forEachByte |
(u8) -> void |
void |
Iterate over every byte |
map |
(char) -> char |
String |
Transform scalar char values |
mapBytes |
(u8) -> u8 |
String |
Transform bytes directly |
findByte |
(u8) -> boolean |
Option<u64> |
Index of first matching byte |
findLastByte |
(u8) -> boolean |
Option<u64> |
Index of last matching byte |
trimWhere |
(u8) -> boolean |
String |
Strip leading/trailing bytes |
split |
(u8) -> boolean |
Vector<String> |
Split into segments |
All callbacks are @noescape: the closure must not outlive the method call.
Byte Conversion
| Method | Returns | Description |
|---|---|---|
toBytes |
Vector<u8> |
Copy bytes into a byte vector |
toChars |
Vector<char> |
Copy bytes into a char vector |
Numeric Conversions
String::create is overloaded for all numeric primitives (i8 through
f64), plus str. Extension methods provide .toString() on every
primitive type.
Example
import String from "std::string";
import Io from "std::io";
function main(): i32 {
let greeting: String = String::create("hello");
let world: str = " world";
let msg: String = greeting.concat(world);
Io::println(msg);
// Byte-level: trim whitespace and split on commas
let csv: String = String::create(" a,b,c ");
let trimmed: String = csv.trimWhere((b: u8): boolean -> { return b == 32; });
let parts: Vector<String> = trimmed.split((b: u8): boolean -> { return b == 44; });
// parts: ["a", "b", "c"]
return 0;
}
namespace __stringFunctions
function ignis_string_byte_at(s: &String, idx: u64): u8Returns the raw byte at idx; callers wrap bounds checks.
function ignis_string_char_at(s: &String, idx: u64, outEnd: &mut u64): charReturns the scalar at byte idx and writes the exclusive end byte offset.
function ignis_string_clear(s: &mut String): voidClears s without freeing its capacity.
function ignis_string_compare(a: &String, b: &String): i32Compares two strings byte-wise using runtime ordering semantics.
function ignis_string_contains(haystack: &String, needle: &String): booleanReturns whether haystack contains needle.
function ignis_string_cstr(s: &String): strReturns the NUL-terminated borrowed view of s.
function ignis_string_drop(s: &mut String): voidReleases the runtime allocation held by s.
function ignis_string_index_of(haystack: &String, needle: &String): i64Returns the byte index of needle in haystack, or -1.
function ignis_string_init_clone(out: &mut String, s: &String): voidInitializes out as a deep clone of s.
function ignis_string_init_concat(out: &mut String, a: &String, b: &String): voidInitializes out as the concatenation of a and b.
function ignis_string_init_from_cstr(out: &mut String, s: str): voidInitializes out by copying a NUL-terminated string view.
function ignis_string_init_from_f32(out: &mut String, value: f32): voidInitializes out with the decimal text of an f32.
function ignis_string_init_from_f64(out: &mut String, value: f64): voidInitializes out with the decimal text of an f64.
function ignis_string_init_from_i16(out: &mut String, value: i16): voidInitializes out with the decimal text of an i16.
function ignis_string_init_from_i32(out: &mut String, value: i32): voidInitializes out with the decimal text of an i32.
function ignis_string_init_from_i64(out: &mut String, value: i64): voidInitializes out with the decimal text of an i64.
function ignis_string_init_from_i8(out: &mut String, value: i8): voidInitializes out with the decimal text of an i8.
function ignis_string_init_from_len(out: &mut String, s: *mut u8, len: u64): voidInitializes out by copying len bytes from s, preserving interior NULs.
function ignis_string_init_from_u16(out: &mut String, value: u16): voidInitializes out with the decimal text of a u16.
function ignis_string_init_from_u32(out: &mut String, value: u32): voidInitializes out with the decimal text of a u32.
function ignis_string_init_from_u64(out: &mut String, value: u64): voidInitializes out with the decimal text of a u64.
function ignis_string_init_from_u8(out: &mut String, value: u8): voidInitializes out with the decimal text of a u8.
function ignis_string_init_new(out: &mut String): voidInitializes out as an empty runtime string.
function ignis_string_init_substring(out: &mut String, s: &String, start: i64, len: i64): voidInitializes out from a byte-range substring of s.
function ignis_string_init_to_lower(out: &mut String, s: &String): voidInitializes out as an ASCII lowercase copy of s.
function ignis_string_init_to_upper(out: &mut String, s: &String): voidInitializes out as an ASCII uppercase copy of s.
function ignis_string_init_with_capacity(out: &mut String, cap: u64): voidInitializes out as an empty runtime string with at least cap bytes.
function ignis_string_len(s: &String): u64Returns the byte length of s.
function ignis_string_push_byte(s: &mut String, c: u8): voidAppends one raw byte to s.
function ignis_string_push_char(s: &mut String, c: char): voidAppends one Unicode-scalar char to s as UTF-8.
function ignis_string_push_cstr(s: &mut String, cstr: str): voidAppends a NUL-terminated string view to s.
function ignis_string_push_str(s: &mut String, other: &String): voidAppends the owned bytes from other to s.
function ignis_string_reserve(s: &mut String, additional: u64): voidReserves room for at least additional more bytes.
namespace Utf8UTF-8 byte-backed decoding helpers for String.
Functions
function byteAt(source: &String, index: u64): Option<u8>Returns the byte at index from a string, if present.
function continuation(source: &String, index: u64): Option<u32>Returns a UTF-8 continuation payload at index, if the byte is valid.
function decodeAt(source: &String, offset: u64, outEndByte: &mut u64): Option<char>Decodes one UTF-8 scalar from source at byte offset.
Invalid byte sequences return U+FFFD covering the first invalid byte so the caller can make progress while preserving diagnostic offsets.
function invalidUtf8(startByte: u64, outEndByte: &mut u64): charReturns a replacement character and advances by one byte.
Records
record StringOwned, heap-backed UTF-8 byte string.
The data buffer is heap-allocated and freed when the String is dropped.
The record stores an owned UTF-8 byte buffer with byte-counted length and
a trailing NUL byte for interop. Character APIs decode UTF-8 scalars while
byte APIs remain explicit.
Implements Drop for automatic cleanup and Clone for deep copies.
Fields
data- Raw pointer to the heap byte buffer (nullwhen uninitialized).len- Number of bytes currently stored.cap- Total capacity of the buffer in bytes.
Example
import String from "std::string";
let s: String = String::create("ignis");
let len: u64 = s.length(); // 5
let c: Option<char> = s.charAt(0); // Option::SOME('i')
data: *mut u8Raw pointer to the heap byte buffer (null when uninitialized).
len: u64Number of bytes currently stored.
cap: u64Total capacity of the buffer in bytes.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static create(value: u8): StringCreates an owned String from a u8.
static hexDigit(value: u8): charstatic new(): StringCreates a new empty string with default capacity.
Example
import String from "std::string";
let mut s: String = String::new();
s.pushStr("hello");
static withCapacity(capacity: u64): StringCreates a new empty string with at least capacity bytes reserved.
Use this when you know roughly how many bytes the string will hold,
to avoid repeated reallocations during push/pushStr/pushChar.
Arguments
capacity- Minimum number of bytes to reserve.
Example
import String from "std::string";
let mut s: String = String::withCapacity(256);
s.pushStr("pre-allocated buffer");
asBytes(&self): u8[]Borrows the underlying bytes as a non-owning u8[] slice.
The returned slice preserves interior NUL bytes because String stores
an explicit byte length. It is invalidated if the string is mutated in a
way that reallocates its backing buffer.
byteAt(&self, index: u64): Option<u8>Returns the raw byte at index, or Option::NONE when out of range.
charAt(&self, index: u64, outEndByte: &mut u64): Option<char>Returns the decoded UTF-8 scalar at index and writes its exclusive byte end.
Use charAt() with byte offsets when you need Unicode scalar traversal
without exposing extra public span or cursor records.
charAt(&self, index: u64, outEndByte: &mut u64): Option<char>Returns the decoded UTF-8 scalar at index and writes its exclusive byte end.
Use charAt() with byte offsets when you need Unicode scalar traversal
without exposing extra public span or cursor records.
charAt(&self, index: u64, outEndByte: &mut u64): Option<char>Returns the decoded UTF-8 scalar at index and writes its exclusive byte end.
Use charAt() with byte offsets when you need Unicode scalar traversal
without exposing extra public span or cursor records.
charAt(&self, index: u64, outEndByte: &mut u64): Option<char>Returns the decoded UTF-8 scalar at index and writes its exclusive byte end.
Use charAt() with byte offsets when you need Unicode scalar traversal
without exposing extra public span or cursor records.
clear(&mut self): voidResets the string length to 0 without releasing the backing buffer.
Subsequent pushes reuse the existing capacity.
Example
import String from "std::string";
let mut s: String = String::create("hello");
s.clear();
// s.length() == 0, but capacity is still available
clone(&self): StringCreates a deep copy of this string.
Allocates a new buffer and copies all bytes. The returned String
is completely independent from self.
Example
import String from "std::string";
let original: String = String::create("hello");
let copy: String = original.clone();
// original and copy are independent heap allocations
compare(&self, other: &String): i32Lexicographically compares this string with other byte-by-byte.
Returns
- Negative if
self < other - Zero if
self == other - Positive if
self > other
Example
import String from "std::string";
let a: String = String::create("apple");
let b: String = String::create("banana");
let cmp: i32 = a.compare(&b); // negative (a < b)
concat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): Stringconcat(&self, value: u8): StringconcatStr(&self, other: str): StringAlias overload for concat(str).
contains(&self, needle: &String): booleanReturns true if needle appears anywhere in this string.
Arguments
needle- The substring to search for.
Example
import String from "std::string";
let s: String = String::create("hello world");
let needle: String = String::create("world");
let found: boolean = s.contains(&needle); // true
drop(&mut self): voidReleases the backing byte buffer and resets all fields.
Called automatically when the String goes out of scope via
@implements(Drop). Can also be called manually to release
memory early.
equals(&self, other: str): booleanReturns whether this owned string has the same NUL-terminated bytes as other.
equals(&self, other: str): booleanReturns whether this owned string has the same NUL-terminated bytes as other.
findByte(&self, predicate: fn(u8) -> boolean): Option<u64>Returns the byte index of the first byte for which predicate
returns true, or Option::NONE if no byte matches.
Short-circuits: stops scanning at the first match.
Arguments
predicate- Callback that receives a byte and returnstrueto indicate a match.
Returns
Option::SOME(index) for the first matching byte, or Option::NONE.
Example
import String from "std::string";
import Option from "std::option";
let s: String = String::create("hello world");
// Find the first space (byte 32)
let idx: Option<u64> = s.findByte((b: u8): boolean -> { return b == 32; });
// idx == Option::SOME(5)
// Find a byte that doesn't exist
let missing: Option<u64> = s.findByte((b: u8): boolean -> { return b == 0; });
// missing == Option::NONE
findLastByte(&self, predicate: fn(u8) -> boolean): Option<u64>Returns the byte index of the last byte matching predicate,
or Option::NONE if no byte matches.
Scans the string from back to front.
Arguments
predicate- Callback that receives a byte and returnstrueif it matches the search criterion.
Example
import String from "std::string";
import Option from "std::option";
let s: String = String::create("/usr/local/bin");
// Find the last slash (byte 47)
let idx: Option<u64> = s.findLastByte((b: u8): boolean -> { return b == 47; });
// idx == Option::SOME(10)
// Find a byte that doesn't exist
let missing: Option<u64> = s.findLastByte((b: u8): boolean -> { return b == 0; });
// missing == Option::NONE
forEach(&self, callback: fn(char) -> void): voidCalls callback once for each decoded UTF-8 scalar.
Invalid UTF-8 bytes are surfaced as U+FFFD and consume one byte so the traversal always makes forward progress.
forEachByte(&self, callback: fn(u8) -> void): voidCalls callback once for each byte in the string, in order.
The callback receives the raw byte value (u8), not a character
reference. This is useful for byte-level inspection or accumulation.
Arguments
callback- Function invoked with each byte value.
Example
import String from "std::string";
let s: String = String::create("abc");
// Print each byte value (97, 98, 99)
s.forEachByte((b: u8): void -> {
Io::println(b);
});
forEachChar(&self, callback: fn(char) -> void): voidCompatibility alias for forEach().
hash(&self, hasher: &mut Hasher): voidindexOf(&self, needle: &String): Option<u64>Returns the byte index of the first occurrence of needle, or NONE.
Arguments
needle- The substring to search for.
Returns
Option::SOME(index) with the byte offset of the first match, or
Option::NONE if needle is not found.
Example
import String from "std::string";
import Option from "std::option";
let s: String = String::create("hello world");
let needle: String = String::create("world");
let idx: Option<u64> = s.indexOf(&needle); // Option::SOME(6)
indexOfCharFrom(&self, target: char, startByte: u64): Option<u64>Returns the byte offset of target at or after startByte.
length(&self): u64Returns the string length in bytes.
Example
import String from "std::string";
let s: String = String::create("hello");
let len: u64 = s.length(); // 5
lines(&self): Vector<String>Splits the string into owned lines without the trailing \n byte.
map(&self, mapper: fn(char) -> char): StringMaps each decoded UTF-8 scalar through mapper and returns a new String.
Invalid UTF-8 bytes are surfaced to mapper as U+FFFD and consume one
byte in the source string.
mapBytes(&self, mapper: fn(u8) -> u8): StringMaps each byte through mapper and returns a new String.
push(&mut self, other: &String): voidAppends the contents of another String to this string.
Arguments
other- The String whose bytes are appended.
Example
import String from "std::string";
let mut s: String = String::create("hello");
let suffix: String = String::create(" world");
s.push(&suffix);
// s is now "hello world"
pushByte(&mut self, c: u8): voidAppends a raw byte to the end of this string.
pushChar(&mut self, c: char): voidAppends one Unicode scalar to the end of this string.
May trigger a reallocation if the buffer is full.
Arguments
c- The Unicode scalar to append.
Example
import String from "std::string";
let mut s: String = String::create("ab");
s.pushChar('c'); // s is now "abc"
pushRepeated(&mut self, text: str, count: u64): voidAppends text count times.
pushStr(&mut self, s: str): voidAppends a str literal to the end of this string.
Arguments
s- Thestrslice to append.
Example
import String from "std::string";
let mut s: String = String::new();
s.pushStr("hello");
s.pushStr(" world");
// s is now "hello world"
reserve(&mut self, additional: u64): voidEnsures the buffer has room for at least additional more bytes
beyond the current length.
If the current capacity is already sufficient, this is a no-op. Otherwise the buffer is reallocated.
Arguments
additional- Number of extra bytes to guarantee.
Example
import String from "std::string";
let mut s: String = String::new();
s.reserve(1024);
// s can now hold at least 1024 bytes without reallocation
split(&self, predicate: fn(u8) -> boolean): Vector<String>Splits the string at each byte where predicate returns true.
The matching (separator) bytes are not included in the resulting segments. Adjacent separators produce empty strings. A trailing separator produces a trailing empty string.
Arguments
predicate- Callback that receives a byte and returnstrueat split points.
Returns
A Vector<String> of segments. The caller owns the vector and all
the strings within it.
How It Works
"a,b,,c" with predicate: b == 44 (comma)
a , b , , c
^ ← segment "a"
^ ← split
^ ← segment "b"
^ ← split
^ ← split (empty segment "")
^← segment "c"
Result: ["a", "b", "", "c"]
Example
import String from "std::string";
import Vector from "std::vector";
let csv: String = String::create("alice,bob,charlie");
// Split on commas (byte 44)
let parts: Vector<String> = csv.split((b: u8): boolean -> {
return b == 44;
});
// parts: ["alice", "bob", "charlie"]
// Split on spaces
let words: String = String::create("hello world foo");
let tokens: Vector<String> = words.split((b: u8): boolean -> {
return b == 32;
});
// tokens: ["hello", "world", "foo"]
startsWith(&self, prefix: str): booleanReturns whether this string starts with the NUL-terminated prefix bytes.
substring(&self, start: i64, length: i64): StringReturns a new string containing length bytes starting at byte
offset start.
Arguments
start- Zero-based byte offset to begin extraction.length- Number of bytes to extract.
Example
import String from "std::string";
let s: String = String::create("hello world");
let sub: String = s.substring(6, 5); // "world"
substringBytes(&self, startByte: u64, endByte: u64): StringReturns a new string containing bytes in startByte..endByte.
This is an owned substring. It does not return a borrowed str, because
primitive str is NUL-terminated and does not carry an explicit length.
toBytes(&self): Vector<u8>Copies the underlying bytes into a new Vector<u8>.
toChars(&self): Vector<char>Copies the underlying bytes into a new Vector<char>.
This is a byte-oriented compatibility helper: each raw byte is cast to
char directly without UTF-8 decoding. Use charAt() with byte offsets
when you need Unicode scalar traversal.
toLowerCase(&self): StringReturns a new string with all ASCII uppercase bytes converted to lowercase. Non-ASCII bytes are copied unchanged.
Example
import String from "std::string";
let s: String = String::create("Hello");
let lower: String = s.toLowerCase(); // "hello"
toStr(&self): strReturns an immutable str view (C const char*) of this string’s data.
toStr() is a zero-copy interop view. The returned str borrows the
String’s buffer and is valid as long as the String is alive and not
mutated, but C-string consumers may stop at the first interior NUL byte.
Example
import String from "std::string";
let s: String = String::create("hello");
let view: str = s.toStr(); // "hello"
toUpperCase(&self): StringReturns a new string with all ASCII lowercase bytes converted to uppercase. Non-ASCII bytes are copied unchanged.
Example
import String from "std::string";
let s: String = String::create("Hello");
let upper: String = s.toUpperCase(); // "HELLO"
toVectorChars(&self): Vector<char>Compatibility alias for toChars().
trimAsciiWhitespace(&self): StringReturns a copy with leading and trailing ASCII whitespace removed.
This trims space, line feed, carriage return, and tab. It is intentionally ASCII-only for parsers and compatibility fixture processing.
trimWhere(&self, predicate: fn(u8) -> boolean): StringReturns a new string with leading and trailing bytes stripped
where predicate returns true.
Scans inward from both ends, removing bytes as long as the predicate matches. Returns an empty string if all bytes are stripped.
Arguments
predicate- Callback that receives a byte and returnstrueto strip it.
Returns
A new String with the outer matching bytes removed. The original
string is unchanged.
How It Works
" hello " with predicate: b == 32 (space)
Step 1 — scan from left:
[ ][ ][h][e][l][l][o][ ][ ]
^ ^ ← strip (space)
^ ← stop (not space)
start = 2
Step 2 — scan from right:
[ ][ ][h][e][l][l][o][ ][ ]
^ ^ ← strip (space)
^ ← stop (not space)
end = 6
Result: substring(2, 5) → "hello"
Example
import String from "std::string";
let s: String = String::create(" hello ");
// Trim spaces (byte 32)
let trimmed: String = s.trimWhere((b: u8): boolean -> { return b == 32; });
// trimmed == "hello"
// Trim tabs and spaces
let mixed: String = String::create("\t hello \t");
let clean: String = mixed.trimWhere((b: u8): boolean -> {
return b == 32 || b == 9;
});
// clean == "hello"
Functions
function asBytes(value: str): u8[]Borrows a NUL-terminated str as a non-owning byte slice.
Because primitive str does not carry a length in v0.4, this scans until
the first NUL byte. Bytes after an interior NUL are not part of the slice.
function stringAsciiWhitespace(value: char): booleanfunction toChars(value: str): Vector<char>Copies a NUL-terminated str into an owned Vector<char>.
This remains byte-oriented: each byte is cast to char directly and the
scan stops at the first terminating NUL.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.
function toString(value: str): StringCopies a borrowed str into an owned String.