Module imm/string
Immutable, thread-safe UTF-8 string type.
Uses atomic reference counting for safe sharing across threads.
All "modification" operations return a new string; the original is
unchanged — and each one allocates, since there is no shared structure
between two ImmStrings: this is a flat byte buffer, cheap to CLONE
(a refcount bump) and O(n) to derive from.
Offsets are BYTES, as everywhere in Yo after D4: len, byte_at,
slice, index_of and the Pattern-shaped predicates all count bytes,
while chars/char_indices/at are the rune-aware vocabulary and
is_char_boundary/floor_char_boundary/ceil_char_boundary snap an
arbitrary offset to one. docs/en-US/STRINGS.md is the full contract.
Stability
unstable — for one release only, and for one reason: D10 flipped
replace to replace EVERY occurrence, and the two aliases it displaced
(replace_first, replace_all) are still here, marked deprecated at
their definitions and due to be removed. Nothing else on this surface is
moving — the byte-offset vocabulary is D4's and matches String's
method for method, which is what makes the two interchangeable at a call
site. Freezing follows the alias removal.
Types
Immutable, thread-safe UTF-8 string.
Uses atomic RC — cheap to clone, safe to share across threads. No mutable operations. All "modification" methods return a new ImmString.
Empty strings are zero-allocation (null pointer, zero length).
Fields
| Name | Type | Description |
|---|---|---|
_ptr | ?(*(u8)) | |
_len | usize | |
_capacity | usize |
Trait Implementations
impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
impl(ImmString, Acyclic())
impl(ImmString, Dispose(...))
dispose : (ImmString) fn(self : ImmString) -> unitRelease the resources self owns — a file descriptor, a socket, a lock,
a buffer the allocator handed out. Called automatically when the last
reference to the value goes away, so an implementor never calls it
directly and must tolerate being the only one who ever does.
It must be safe to run exactly once: the runtime calls it at refcount
zero, and a type that also exposes an explicit close/release is
responsible for making the second call a no-op.
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString |
Returns: unit
impl(ImmString, ...)
new : (ImmString) fn() -> ImmStringCreate a new empty string (zero allocation).
Returns: ImmString
from : (ImmString) fn(s : str) -> ImmStringCreate a string from a str slice (copies the bytes).
Parameters
| Name | Type | Notes |
|---|---|---|
s | str |
Returns: ImmString
from_string : (ImmString) fn(s : String) -> ImmStringlen : (ImmString) fn(self : ImmString) -> usizeNumber of BYTES in this string's UTF-8 encoding — O(1).
BYTE basis, the unit every index on this type speaks: slice,
index_of, byte_at and at all take or return byte offsets, and it
agrees with std/string's String.len(), str.len() and Rust's
str::len().
For "how many runes" use chars().count() (O(n)); to walk runes together with
the byte offset each one starts at, use char_indices().
(plans/archive/STD_API_AUDIT_D4_PLAN.md D4 PR 4 — this returned a RUNE count
before that flip, and a bytes_len() alias carried the byte count; the
alias was DELETED with the flip because it had no consumer left.)
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString |
Returns: usize
is_empty : (ImmString) fn(self : ImmString) -> boolas_str : (ImmString) fn(self : ImmString) -> strGet a str view of the string's bytes (zero-copy borrow).
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString |
Returns: str
byte_at : (ImmString) fn(self : ImmString, index : usize) -> Option(u8)concat : (ImmString) fn(self : ImmString, other : ImmString) -> ImmStringslice : (ImmString) fn(self : ImmString, start : usize, end : usize) -> ImmStringstarts_with : (ImmString) fn(self : ImmString, prefix : ImmString) -> boolends_with : (ImmString) fn(self : ImmString, suffix : ImmString) -> boolindex_of : (ImmString) fn(self : ImmString, needle : ImmString, from_index : usize) -> Option(usize)contains : (ImmString) fn(self : ImmString, needle : ImmString) -> boolsplit : (ImmString) fn(self : ImmString, sep : ImmString) -> List(ImmString)_is_whitespace : (ImmString) fn(b : u8) -> boolCheck if a byte is ASCII whitespace.
Parameters
| Name | Type | Notes |
|---|---|---|
b | u8 |
Returns: bool
trim : (ImmString) fn(self : ImmString) -> ImmStringtrim_start : (ImmString) fn(self : ImmString) -> ImmStringtrim_end : (ImmString) fn(self : ImmString) -> ImmStringto_lowercase : (ImmString) fn(self : ImmString) -> ImmStringto_uppercase : (ImmString) fn(self : ImmString) -> ImmString_replace_scan : (ImmString) fn(self : ImmString, search : ImmString, replacement : ImmString, limit : usize, bounded : bool) -> ImmStringShared left-to-right scanner behind replace, replacen and
replace_first. When bounded is true it stops after limit
replacements and copies the rest verbatim; limit == 0 with bounded
replaces nothing. An empty search never matches (it would not
terminate), matching the pre-D10 replace_all.
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString | |
search | ImmString | |
replacement | ImmString | |
limit | usize | |
bounded | bool |
Returns: ImmString
replace : (ImmString) fn(self : ImmString, search : ImmString, replacement : ImmString) -> ImmStringReplace EVERY occurrence of search with replacement (D10 — Rust's
str::replace). An empty search returns self unchanged.
NOTE this replaced only the FIRST occurrence before D10. Use replacen
for a bounded count.
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString | |
search | ImmString | |
replacement | ImmString |
Returns: ImmString
replacen : (ImmString) fn(self : ImmString, search : ImmString, replacement : ImmString, count : usize) -> ImmStringreplace_first : (ImmString) fn(self : ImmString, search : ImmString, replacement : ImmString) -> ImmStringreplace_all : (ImmString) fn(self : ImmString, search : ImmString, replacement : ImmString) -> ImmStringrepeat : (ImmString) fn(self : ImmString, n : usize) -> ImmString_decode_rune_at : (ImmString) fn(self : ImmString, byte_index : usize) -> Option(rune)at : (ImmString) fn(self : ImmString, index : usize) -> Option(rune)Decode the rune that STARTS at byte offset index.
BYTE basis (plans/archive/STD_API_AUDIT_D4_PLAN.md D4 PR 4 — this took a
rune index before the flip; it now mirrors std/string's String.at).
.None is returned for the three ways a byte offset can fail to name a
rune: at or past len(), inside a rune (a UTF-8 continuation byte), or
on bytes that do not decode.
while(i < s.len(), { s.at(i) }) therefore visits continuation bytes and
yields .None at each of them — use char_indices() (or chars()) to
walk runes.
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString | |
index | usize |
Returns: Option(rune)
impl(ImmString, ...)
chars : (ImmString) fn(self : ImmString) -> ImmStringCharschar_indices : (ImmString) fn(self : ImmString) -> ImmStringCharIndicesIterate (byte_offset, rune) pairs — Rust's char_indices().
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString |
Returns: ImmStringCharIndices
is_char_boundary : (ImmString) fn(self : ImmString, index : usize) -> boolTrue when byte offset index sits on a rune boundary.
0 and len() are always boundaries; an index past the end never
is. Mirrors Rust's str::is_char_boundary.
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString | |
index | usize |
Returns: bool
floor_char_boundary : (ImmString) fn(self : ImmString, index : usize) -> usizeThe largest byte offset <= index that sits on a rune boundary.
index is clamped to len() first, so the answer is always a
boundary of THIS string.
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString | |
index | usize |
Returns: usize
ceil_char_boundary : (ImmString) fn(self : ImmString, index : usize) -> usizeThe smallest byte offset >= index that sits on a rune boundary.
index at or past len() clamps to len(), which is a
boundary, so this always terminates on a valid one.
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString | |
index | usize |
Returns: usize
try_substring : (ImmString) fn(self : ImmString, start : usize, end : usize) -> Option(ImmString)Byte-range slice that REFUSES a bad range instead of clamping it —
Rust's s.get(start..end), and the checked counterpart of slice.
.None for start > end, for end past len(), and for an
endpoint inside a rune (which would produce invalid UTF-8). An empty but
valid range yields .Some of the empty string.
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmString | |
start | usize | |
end | usize |
impl(ImmString, Eq(ImmString)(...))
impl(ImmString, ...)
eq_str : (ImmString) fn(self : ImmString, other : str) -> boolimpl(ImmString, Ord(ImmString)(...))
impl(ImmString, Hash(...))
impl(ImmString, ToString(...))
to_string : (ImmString) fn(self : ImmString) -> Stringimpl(ImmString, Default(...))
default : (ImmString) fn() -> ImmStringThe default value of the type.
Returns: ImmString
Methods
== : (ImmString) fn(self : ImmString, other : ImmString) -> bool!= : (ImmString) fn(self : ImmString, other : ImmString) -> bool< : (ImmString) fn(self : ImmString, other : ImmString) -> bool<= : (ImmString) fn(self : ImmString, other : ImmString) -> bool> : (ImmString) fn(self : ImmString, other : ImmString) -> bool>= : (ImmString) fn(self : ImmString, other : ImmString) -> boolRune iterator for ImmString — created by chars().
Fields
| Name | Type | Description |
|---|---|---|
_string | ImmString | |
_byte_index | usize |
Trait Implementations
impl(generic(I : Type), where(I <: Iterator), I : (Iterator))
impl(generic(I : Type), where(I <: Iterator), I : (Iterator))
map : fn(generic(A, B, F) self : I : (Iterator), f : F : (Fn(A) -> B)) -> IterMap(I : (Iterator), B, F : (Fn(A) -> B))filter : fn(generic(A, F) self : I : (Iterator), f : F : (Fn(A) -> bool)) -> IterFilter(I : (Iterator), F : (Fn(A) -> bool))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) | |
f | F : (Fn(A) -> bool) |
Returns: IterFilter(I : (Iterator), F : (Fn(A) -> bool))
take : fn(generic(A) self : I : (Iterator), n : usize) -> IterTake(I : (Iterator))skip : fn(generic(A) self : I : (Iterator), n : usize) -> IterSkip(I : (Iterator))enumerate : fn(generic(A) self : I : (Iterator)) -> IterEnumerate(I : (Iterator))zip : fn(generic(A, J, B) self : I : (Iterator), other : J : (Iterator)) -> IterZip(I : (Iterator), J : (Iterator))fold : fn(generic(A, Acc, F) self : I : (Iterator), init : Acc, f : F : (Fn(Acc, A) -> Acc)) -> Accfor_each : fn(generic(A, F) self : I : (Iterator), f : F : (Fn(A) -> unit)) -> unitcount : fn(generic(A) self : I : (Iterator)) -> usizeany : fn(generic(A, F) self : I : (Iterator), pred : F : (Fn(A) -> bool)) -> boolall : fn(generic(A, F) self : I : (Iterator), pred : F : (Fn(A) -> bool)) -> boolfind : fn(generic(A, F) self : I : (Iterator), pred : F : (Fn(A) -> bool)) -> Option(A)position : fn(generic(A, F) self : I : (Iterator), pred : F : (Fn(A) -> bool)) -> Option(usize)last : fn(generic(A) self : I : (Iterator)) -> Option(A)nth : fn(generic(A) self : I : (Iterator), n : usize) -> Option(A)sum : fn(generic(A) self : I : (Iterator)) -> A : ((Output : Type, + : fn(lhs : Self, rhs : A) -> Output) + Default)min : fn(generic(A) self : I : (Iterator)) -> Option(A : ((< : fn(lhs : Self : ((== : fn(lhs : Self, rhs : A) -> bool, != : fn(lhs : Self, rhs : A) -> bool)), rhs : A) -> bool, <= : fn(lhs : Self, rhs : A) -> bool, > : fn(lhs : Self, rhs : A) -> bool, >= : fn(lhs : Self, rhs : A) -> bool, cmp : fn(lhs : Self, rhs : A) -> Ordering)))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) |
Returns: Option(A : ((< : fn(lhs : Self : ((== : fn(lhs : Self, rhs : A) -> bool, != : fn(lhs : Self, rhs : A) -> bool)), rhs : A) -> bool, <= : fn(lhs : Self, rhs : A) -> bool, > : fn(lhs : Self, rhs : A) -> bool, >= : fn(lhs : Self, rhs : A) -> bool, cmp : fn(lhs : Self, rhs : A) -> Ordering)))
max : fn(generic(A) self : I : (Iterator)) -> Option(A : ((< : fn(lhs : Self : ((== : fn(lhs : Self, rhs : A) -> bool, != : fn(lhs : Self, rhs : A) -> bool)), rhs : A) -> bool, <= : fn(lhs : Self, rhs : A) -> bool, > : fn(lhs : Self, rhs : A) -> bool, >= : fn(lhs : Self, rhs : A) -> bool, cmp : fn(lhs : Self, rhs : A) -> Ordering)))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) |
Returns: Option(A : ((< : fn(lhs : Self : ((== : fn(lhs : Self, rhs : A) -> bool, != : fn(lhs : Self, rhs : A) -> bool)), rhs : A) -> bool, <= : fn(lhs : Self, rhs : A) -> bool, > : fn(lhs : Self, rhs : A) -> bool, >= : fn(lhs : Self, rhs : A) -> bool, cmp : fn(lhs : Self, rhs : A) -> Ordering)))
chain : fn(generic(A, J) self : I : (Iterator), other : J : (Iterator)) -> IterChain(I : (Iterator), J : (Iterator))take_while : fn(generic(A, F) self : I : (Iterator), f : F : (Fn(A) -> bool)) -> IterTakeWhile(I : (Iterator), F : (Fn(A) -> bool))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) | |
f | F : (Fn(A) -> bool) |
Returns: IterTakeWhile(I : (Iterator), F : (Fn(A) -> bool))
skip_while : fn(generic(A, F) self : I : (Iterator), f : F : (Fn(A) -> bool)) -> IterSkipWhile(I : (Iterator), F : (Fn(A) -> bool))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) | |
f | F : (Fn(A) -> bool) |
Returns: IterSkipWhile(I : (Iterator), F : (Fn(A) -> bool))
filter_map : fn(generic(A, B, F) self : I : (Iterator), f : F : (Fn(A) -> Option(B))) -> IterFilterMap(I : (Iterator), B, F : (Fn(A) -> Option(B)))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) | |
f | F : (Fn(A) -> Option(B)) |
Returns: IterFilterMap(I : (Iterator), B, F : (Fn(A) -> Option(B)))
peekable : fn(generic(A) self : I : (Iterator)) -> IterPeekable(I : (Iterator), A)collect : fn(self : I : (Iterator), C : Type) -> C : (FromIterator)impl(ImmStringChars, Iterator(...))
Item : runenext : (ImmStringChars) fn(self : ImmStringChars) -> Option(rune)Advance the iterator and return the next value, or None when exhausted.
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmStringChars |
Returns: Option(rune)
(byte_offset, rune) iterator for ImmString — created by
char_indices(). Walks in lockstep with ImmStringChars.
Fields
| Name | Type | Description |
|---|---|---|
_string | ImmString | |
_byte_index | usize |
Trait Implementations
impl(generic(I : Type), where(I <: Iterator), I : (Iterator))
impl(generic(I : Type), where(I <: Iterator), I : (Iterator))
map : fn(generic(A, B, F) self : I : (Iterator), f : F : (Fn(A) -> B)) -> IterMap(I : (Iterator), B, F : (Fn(A) -> B))filter : fn(generic(A, F) self : I : (Iterator), f : F : (Fn(A) -> bool)) -> IterFilter(I : (Iterator), F : (Fn(A) -> bool))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) | |
f | F : (Fn(A) -> bool) |
Returns: IterFilter(I : (Iterator), F : (Fn(A) -> bool))
take : fn(generic(A) self : I : (Iterator), n : usize) -> IterTake(I : (Iterator))skip : fn(generic(A) self : I : (Iterator), n : usize) -> IterSkip(I : (Iterator))enumerate : fn(generic(A) self : I : (Iterator)) -> IterEnumerate(I : (Iterator))zip : fn(generic(A, J, B) self : I : (Iterator), other : J : (Iterator)) -> IterZip(I : (Iterator), J : (Iterator))fold : fn(generic(A, Acc, F) self : I : (Iterator), init : Acc, f : F : (Fn(Acc, A) -> Acc)) -> Accfor_each : fn(generic(A, F) self : I : (Iterator), f : F : (Fn(A) -> unit)) -> unitcount : fn(generic(A) self : I : (Iterator)) -> usizeany : fn(generic(A, F) self : I : (Iterator), pred : F : (Fn(A) -> bool)) -> boolall : fn(generic(A, F) self : I : (Iterator), pred : F : (Fn(A) -> bool)) -> boolfind : fn(generic(A, F) self : I : (Iterator), pred : F : (Fn(A) -> bool)) -> Option(A)position : fn(generic(A, F) self : I : (Iterator), pred : F : (Fn(A) -> bool)) -> Option(usize)last : fn(generic(A) self : I : (Iterator)) -> Option(A)nth : fn(generic(A) self : I : (Iterator), n : usize) -> Option(A)sum : fn(generic(A) self : I : (Iterator)) -> A : ((Output : Type, + : fn(lhs : Self, rhs : A) -> Output) + Default)min : fn(generic(A) self : I : (Iterator)) -> Option(A : ((< : fn(lhs : Self : ((== : fn(lhs : Self, rhs : A) -> bool, != : fn(lhs : Self, rhs : A) -> bool)), rhs : A) -> bool, <= : fn(lhs : Self, rhs : A) -> bool, > : fn(lhs : Self, rhs : A) -> bool, >= : fn(lhs : Self, rhs : A) -> bool, cmp : fn(lhs : Self, rhs : A) -> Ordering)))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) |
Returns: Option(A : ((< : fn(lhs : Self : ((== : fn(lhs : Self, rhs : A) -> bool, != : fn(lhs : Self, rhs : A) -> bool)), rhs : A) -> bool, <= : fn(lhs : Self, rhs : A) -> bool, > : fn(lhs : Self, rhs : A) -> bool, >= : fn(lhs : Self, rhs : A) -> bool, cmp : fn(lhs : Self, rhs : A) -> Ordering)))
max : fn(generic(A) self : I : (Iterator)) -> Option(A : ((< : fn(lhs : Self : ((== : fn(lhs : Self, rhs : A) -> bool, != : fn(lhs : Self, rhs : A) -> bool)), rhs : A) -> bool, <= : fn(lhs : Self, rhs : A) -> bool, > : fn(lhs : Self, rhs : A) -> bool, >= : fn(lhs : Self, rhs : A) -> bool, cmp : fn(lhs : Self, rhs : A) -> Ordering)))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) |
Returns: Option(A : ((< : fn(lhs : Self : ((== : fn(lhs : Self, rhs : A) -> bool, != : fn(lhs : Self, rhs : A) -> bool)), rhs : A) -> bool, <= : fn(lhs : Self, rhs : A) -> bool, > : fn(lhs : Self, rhs : A) -> bool, >= : fn(lhs : Self, rhs : A) -> bool, cmp : fn(lhs : Self, rhs : A) -> Ordering)))
chain : fn(generic(A, J) self : I : (Iterator), other : J : (Iterator)) -> IterChain(I : (Iterator), J : (Iterator))take_while : fn(generic(A, F) self : I : (Iterator), f : F : (Fn(A) -> bool)) -> IterTakeWhile(I : (Iterator), F : (Fn(A) -> bool))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) | |
f | F : (Fn(A) -> bool) |
Returns: IterTakeWhile(I : (Iterator), F : (Fn(A) -> bool))
skip_while : fn(generic(A, F) self : I : (Iterator), f : F : (Fn(A) -> bool)) -> IterSkipWhile(I : (Iterator), F : (Fn(A) -> bool))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) | |
f | F : (Fn(A) -> bool) |
Returns: IterSkipWhile(I : (Iterator), F : (Fn(A) -> bool))
filter_map : fn(generic(A, B, F) self : I : (Iterator), f : F : (Fn(A) -> Option(B))) -> IterFilterMap(I : (Iterator), B, F : (Fn(A) -> Option(B)))Parameters
| Name | Type | Notes |
|---|---|---|
self | I : (Iterator) | |
f | F : (Fn(A) -> Option(B)) |
Returns: IterFilterMap(I : (Iterator), B, F : (Fn(A) -> Option(B)))
peekable : fn(generic(A) self : I : (Iterator)) -> IterPeekable(I : (Iterator), A)collect : fn(self : I : (Iterator), C : Type) -> C : (FromIterator)impl(ImmStringCharIndices, Iterator(...))
Item : IterPair(usize, rune)next : (ImmStringCharIndices) fn(self : ImmStringCharIndices) -> Option(IterPair(usize, rune))Advance the iterator and return the next value, or None when exhausted.
Parameters
| Name | Type | Notes |
|---|---|---|
self | ImmStringCharIndices |