string is owned UTF-8 text and char is one Unicode scalar value. Atoll
keeps character positions and encoded-byte positions distinct.
name := "Ångström"
characters := name.length()
encoded := name.len()
first := name.char_at(0)length() walks the UTF-8 buffer and counts Unicode scalars. len() reads the
encoded byte length; byte_length() is its deprecated alias. For non-ASCII
text, the byte length can be larger than the character count.
char_at, first, and last return Option[char], so empty and
out-of-range access cannot panic.
Searching
if path.starts_with("/api/") && path.ends_with(".json") {
use(path)
}
position := text.index_of("needle")The search surface includes contains, starts_with, ends_with,
index_of, and last_index_of. Missing positions are None; successful
positions use character indexes rather than raw byte offsets.
Use equals_ignore_case and compare_to_ignore_case when case-insensitive
comparison is the intended contract. Ordinary == remains content equality.
Slicing
substring(start, end) and slice(range) are byte-indexed and return
substring?, a borrowed byte view into the original string:
match text.substring(0, 5) {
Some(view) => consume(view.to_string())
None => report_bad_range()
}An out-of-bounds range returns None. The current operation does not check
that both endpoints are Unicode character boundaries. Only convert or interpret
the view as text when the byte range is known to be aligned.
The view provides byte length, nested slicing, byte access, owned conversion,
and Bytes behavior. It borrows the parent buffer; use to_string() or
to_bytes() when the result must own its storage.
take, take_last, drop, and drop_last use character counts and return
owned strings.
Transformation
Transformation methods fall into a few groups:
to_upper_case,to_lower_case, and ASCII-only case conversion;capitalize,title_case, trimming, padding, and repetition;replace,replace_first, andreversed;split,split_with_limit,lines,words, andchars;- character
map,filter,any,all,none, andfold.
label := input.trim().to_lower_case()
parts := label.split("-")split_with_limit bounds the number of pieces. enumerate() pairs each
character with its character index. count(predicate) counts matching
characters rather than substring occurrences.
Conversion
to_int, to_float, and to_bool return Option. to_list produces
characters. to_bytes copies into []byte; bytes() returns a borrowed byte
slice.
string.from_bytes(bytes) validates UTF-8 and returns string?.
string.from_chars(chars) and string.join(items, separator) construct text.
match string.from_bytes(packet) {
Some(text) => process(text)
None => reject_invalid_utf8()
}Use the optional parsers for untrusted text. They return None rather than
choosing a fallback or throwing a hidden parsing exception.
Characters
char supports Unicode classification such as is_digit,
is_alphabetic, is_alphanumeric, and is_whitespace, plus case conversion
and u32/int conversion. char.from_u32 rejects invalid Unicode scalar
values:
match char.from_u32(codepoint) {
Some(value) => println(value.to_string())
None => report_invalid_codepoint(codepoint)
}Availability
The prelude contains some declared string operations whose pure-Atoll bodies or backend lowering are still being migrated. Core length, byte access, UTF-8 conversion, searching, ASCII case conversion, equality, and hashing paths are implementation-backed. Before relying on a less common transformation at a deployment boundary, keep a focused compiler/runtime test for that method. A visible prelude signature alone does not guarantee every backend supports the operation yet.