Strings (std::string)
Strings in Zolo are immutable by default. Methods from the std::string module are
available both as method form (s.trim()) and module form (String.trim(s)).
Most operations need no explicit use; when required, import with use std::String.
Whitespace removal
trim() removes whitespace from both sides; trim_start() and trim_end() remove
it from one end only.
E-mail normalisation with .trim().lower() chaining.
// Feature: string.trim — remove whitespace from both ends
// When to use: cleaning user input, normalizing data before comparison.
let raw = " Hello, Zolo! "
// Full trim — removes from both sides.
print("'{raw.trim()}'")
// expected: 'Hello, Zolo!'
// Trim only at the start.
print("'{raw.trim_start()}'")
// expected: 'Hello, Zolo! '
// Trim only at the end.
print("'{raw.trim_end()}'")
// expected: ' Hello, Zolo!'
// Idiomatic chaining — trim + lower normalizes an email.
let email_raw = " ALICE@EXAMPLE.COM "
let email = email_raw.trim().lower()
print(email)
// expected: alice@example.com
// String with no surviving whitespace — trim is a safe no-op.
print("'{"abc".trim()}'")
// expected: 'abc'
Splitting
split(sep) returns an array of strings cut by the separator. Works with
any string as separator — including multi-character separators.
Simple CSV and a two-character :: separator.
// Feature: string.split — splits a string into an array by separator
// When to use: simple CSV parsing, tokens, paths, item lists.
let csv = "apple,banana,cherry"
let parts = csv.split(",")
print(parts[0]) // expected: apple
print(parts[1]) // expected: banana
print(parts[2]) // expected: cherry
print(parts.len()) // expected: 3
// Multi-char separator.
let path = "usr::local::bin"
let segs = path.split("::")
print(segs[0]) // expected: usr
print(segs[2]) // expected: bin
// String without the separator returns a single-element array.
let single = "hello".split(",")
print(single.len()) // expected: 1
print(single[0]) // expected: hello
// Works directly on string literals too.
let xs = "a;b;c".split(";")
print(xs[1]) // expected: b
Replacement
replace(old, new) replaces all occurrences. To remove characters,
replace with "".
Global replacement in CSV; removing dots from a formatted number.
// Feature: string.replace — substitutes occurrences
// When to use: cleaning characters, swapping placeholders, normalizing output.
use std::String
let s = "Hello World"
print(s.replace("World", "Zolo"))
// expected: Hello Zolo
// Global substitution — all occurrences.
let csv = "a,b,c,d"
print(csv.replace(",", " | "))
// expected: a | b | c | d
// Removing characters — just replace with the empty string.
let noisy = "1.234.567,89"
let clean = noisy.replace(".", "")
print(clean)
// expected: 1234567,89
// Module form.
print("foo bar foo".replace("foo", "BAZ"))
// expected: BAZ bar BAZ
// String without the search term remains unchanged.
print("abc".replace("z", "Z"))
// expected: abc
Search and predicates
contains, starts_with and ends_with return booleans — no index handling needed.
File classification by extension using ends_with.
// Feature: string.contains / starts_with / ends_with — predicates
// When to use: validating prefixes/suffixes (paths, URLs, extensions), simple search.
use std::String
let url = "https://zolo.dev/docs"
// contains — substring at any position.
print(url.contains("zolo")) // expected: true
print(url.contains("aws")) // expected: false
// starts_with — prefix.
print(url.starts_with("https://")) // expected: true
print(url.starts_with("http://")) // expected: false
// ends_with — suffix.
print(url.ends_with("/docs")) // expected: true
print(url.ends_with(".html")) // expected: false
// Useful for classifying files.
fn kind(path: str) -> str {
if path.ends_with(".zolo") { return "zolo source" }
if path.ends_with(".md") { return "markdown" }
if path.ends_with(".json") { return "json data" }
return "unknown"
}
print(kind("main.zolo")) // expected: zolo source
print(kind("README.md")) // expected: markdown
print(kind("config.toml")) // expected: unknown
// Module form.
print("banana".contains("nan")) // expected: true
Case conversion
upper() and lower() do not modify the original — they return new strings.
Case-insensitive comparison; .trim().lower() chaining for e-mail.
// Feature: string.upper / string.lower — case conversion
// When to use: normalizing for comparison, display, generating slugs.
use std::String
print("zolo".upper()) // expected: ZOLO
print("ZOLO".lower()) // expected: zolo
// Strings are immutable — original untouched.
let s = "MiSto"
let u = s.upper()
print(u) // expected: MISTO
print(s) // expected: MiSto
// Case-insensitive comparison.
let input = "ADMIN"
let role = "admin"
print(input.lower() == role.lower())
// expected: true
// Module form.
print(String.upper("hello")) // expected: HELLO
print(String.lower("WORLD")) // expected: world
// Chaining — clean and normalize an email in a single step.
let email = " Alice@Example.COM ".trim().lower()
print(email)
// expected: alice@example.com
Character iteration
chars() converts the string into an array of characters. To access the first
character use .chars()[0] — s[0] returns nil.
len(), for c in s.chars() iteration, vowel counting.
// Feature: string.chars / string.len — length and per-char iteration
// When to use: walking a string char by char, counting characters,
// extracting first/last element (note: `s[0]` returns nil — use `.chars()[0]`).
let s = "zolo"
// Number of chars.
print(s.len()) // expected: 4
// Conversion to char array.
let cs = s.chars()
print(cs[0]) // expected: z
print(cs[1]) // expected: o
print(cs[3]) // expected: o
print(cs.len()) // expected: 4
// Iterate char by char.
for c in s.chars() {
print(c)
}
// expected: z
// expected: o
// expected: l
// expected: o
// Get the first char — IDIOM: use `.chars()[0]`, not `s[0]`.
let first = "hello".chars()[0]
print(first) // expected: h
// Count vowels.
fn count_vowels(text: str) -> int {
var n = 0
for c in text.chars() {
if c == "a" || c == "e" || c == "i" || c == "o" || c == "u" {
n = n + 1
}
}
return n
}
print(count_vowels("education")) // expected: 5
Interpolation and format specifiers
String templates use {expr}. Format specifiers — :.2f, :05d,
:#x — control precision, padding and numeric base.
Float with decimal places, integer with leading zeros, hexadecimal with prefix.
// Feature: Interpolation and format specs
// When to use: building messages, aligning tabular output, formatting numbers.
// Basic interpolation.
let name = "Zolo"
let ver = 1
print("Hello, {name} v{ver}")
// expected: Hello, Zolo v1
// Float with N decimal places — `:.Nf`.
let pi = 3.14159265
print("pi = {pi:.2f}") // expected: pi = 3.14
print("pi = {pi:.4f}") // expected: pi = 3.1416
// Integer with zero padding — `:0Nd`.
let id = 42
print("id-{id:05d}") // expected: id-00042
// Hexadecimal with prefix — `:#x`.
let n = 255
print("n = {n:#x}") // expected: n = 0xff
// Combining several format specs in the same string.
let cpu = 87.5
let ram = 2048
print("cpu={cpu:.1f}% ram={ram:0d}MB")
// expected: cpu=87.5% ram=2048MB
// Traditional concatenation still works.
print("hello " + "world")
// expected: hello world
Challenge
Write a function slug(title: str) -> str that converts a title to kebab-case:
trim, lower and replace spaces with -.
See also