Skip to content

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.

01-trim.zolo
Playground
// 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 = "   [email protected]   "
let email = email_raw.trim().lower()
print(email)

// expected: [email protected]

// 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.

02-split.zolo
Playground
// 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.

03-replace.zolo
Playground
// 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.

04-contains-and-tests.zolo
Playground
// 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.

05-case.zolo
Playground
// 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 = "  [email protected]  ".trim().lower()
print(email)
// expected: [email protected]

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.

06-chars.zolo
Playground
// 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.

07-format.zolo
Playground
// 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 -.

enespt-br