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.
// 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 %%.
// 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.
// 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.
See also