Skip to content

10 · Strings

Format Specifiers

Float precision (:.Nf), zero-padding (:0Nd) and hexadecimal (:#x) in interpolation.

On this page

Inside {} interpolation you can add :spec to control value formatting. The supported specifiers are:

Spec Effect Example
:.Nf N decimal places {pi:.2f} → 3.14
:0Nd integer with N digits zero-padded {n:04d} → 0042
:#x integer in hexadecimal with 0x prefix {255:#x} → 0xff

Float precision, zero-padding, and hex in interpolation.

04-format-specs.zolo
Playground
// Feature: Format specs in interpolation

// Syntax: `"{expr:spec}"` — spec after `:`

// When to use: float precision, zero-padding, numeric bases.


// -- Float precision (`.Nf`) ------------------------------------

let pi = 3.14159265
print("{pi:.2f}")  // 3.14

print("{pi:.4f}")  // 3.1416

print("{pi:.0f}")  // 3


// -- Numeric zero-padding (`0Nd`) -------------------------------

let n = 42
print("{n:04d}")  // 0042

print("{n:08d}")  // 00000042


// -- Hex with prefix (`#x`) ------------------------------------

print("{255:#x}")  // 0xff

print("{16:#x}")  // 0x10


// -- Practical combinations ------------------------------------

let price = 9.5
print("$ {price:.2f}")  // $ 9.50


let id = 7
print("ID: {id:04d}")  // ID: 0007


// Coordinates with 3 decimal places.

let x = 1.234567
let y = -0.5
print("({x:.3f}, {y:.3f})")

// expected: (1.235, -0.500)


// Sequential numeric IDs with zero-pad.

var i = 1
while i <= 3 {
  print("user_{i:03d}")
  i = i + 1
}
// expected:

// user_001

// user_002

// user_003


// NOTE: alignment (`>` and `<`) and binary/octal bases are not yet

// supported in the current interpolator. Use precision and zero-pad

// for the most common cases.

For a dynamic format string or several values, import std::string and use string.format. Its printf-style format supports integers, strings, float precision, hexadecimal, width, flags, and a literal %%.

Builds formatted strings with %d, %s, %f, %x, width, flags, and %%.

10-string-format.zolo
Playground
// Feature: `string.format(fmt, args...)` — printf/Lua-style formatting
// Syntax: `string.format("%d %s %.2f", n, s, x)` (after `use std::string`)
// When to use: building formatted strings with width / precision / flags,
// e.g. fixed-decimal floats, zero-padded ints, hex.
//
// Works on every backend: VM, native (Cranelift), and LLVM — all routed
// through the `std::string::format` runtime formatter, so the output is
// identical across engines.

use std::string

// -- Integers (`%d` / `%i`) ------------------------------------
print(string.format("%d", 42))          // 42
print(string.format("%d and %d", 1, 2)) // 1 and 2

// -- Strings (`%s`, via tostring) ------------------------------
print(string.format("hello, %s!", "world")) // hello, world!
print(string.format("%s = %d", "x", 7))      // x = 7

// -- Floats with precision (`%.Nf`) ----------------------------
print(string.format("%.3f", 1.5))   // 1.500
print(string.format("%.0f", 42.0))  // 42
print(string.format("%f", 3.14))    // 3.140000  (bare %f → 6 decimals)

// -- Hex (`%x` / `%X`) -----------------------------------------
print(string.format("%x", 255))  // ff
print(string.format("%X", 255))  // FF

// -- Width, zero-pad and flags ---------------------------------
print(string.format("%05d", 42))   // 00042
print(string.format("%-5d|", 7))   // 7    |
print(string.format("%+d", 9))     // +9

// -- Literal percent (`%%`) ------------------------------------
print(string.format("100%% done"))  // 100% done

// -- Practical: an auto-scaled duration formatter --------------
fn fmt_ms(ms: float) -> str {
  let secs = ms / 1000.0
  if ms >= 1000.0 {
    return string.format("%.3fs", secs)
  } else {
    return string.format("%.3fms", ms)
  }
}

print(fmt_ms(1500.0))  // 1.500s
print(fmt_ms(12.5))    // 12.500ms

// expected:
// 42
// 1 and 2
// hello, world!
// x = 7
// 1.500
// 42
// 3.140000
// ff
// FF
// 00042
// 7    |
// +9
// 100% done
// 1.500s
// 12.500ms

Alignment uses < for the left edge and > for the right edge. Set a width in the format specifier, as shown below.

Dynamic width and precision

Place an integer expression inside braces in an interpolation's format specifier. The value runs first, followed by format parameters once each, from left to right. Width and precision must be integers between zero and one million. Floating precision is applied before alignment. JavaScript emission currently reports format specifiers as unsupported.

15-dynamic-format.zolo
Playground
// Keep currency precision and table width in one presentation policy.

let decimal_places = 2
let label_width = 10
let name = "Coffee"
let price = 12.345
print("|{name:<{label_width}}|{price:.{decimal_places}f}|")
print("|{price:>{label_width}.{decimal_places}f}|")

Dynamic width and precision run on the VM, native and LLVM backends, including the browser VM. Standalone wasm-aot currently rejects them at compile time because it has no format-spec runtime.

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

enespt-br