Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Formatting

Flint provides two formatting packages for different scenarios:

PackageAllocationReturn typeWhen to use
core/fmtNoneResult<(), Error> source APINo-heap checked formatting for sink-style output
std/fmtMay allocateResult<string, Error>Planned owned-string formatting surface

Current Status

core/fmt.write is available today, but the current executable backend slice is narrower than the long-term library design:

  • The compiler recognizes fmt.write(...) as a checked formatting call.
  • The format string must be a compile-time string literal.
  • The compiler handles the variable-argument source shape specially so ordinary variadics are not required in the language.
  • The first argument must be a concrete sink type that implements core/io.Writer.
  • The current executable backend slice resolves the concrete Writer implementation for that sink and lowers the supported sink path.
  • std/fmt.format(...) is reserved in the language and docs, but it is not implemented end to end yet.
  • General runtime trait-dispatch through a trait-typed parameter such as mut out: io.Writer is still separate backend work.

core/fmt.write: No-Heap Checked Formatting

use board/pico as board
use core/fmt

fn main() -> never {
    let uart = board.default_uart().init()
    let value: u32 = 42

    fmt.write(uart, "value={}\r\n", value)

    loop {}
}

Today, the first argument should be a concrete sink handle such as a UART port whose type implements core/io.Writer. The format string must be a compile-time string literal, and the compiler validates it at build time.

core/fmt.write is the preferred formatting path for MCU code because it never allocates.

std/fmt.format: Owned String Formatting

use std/fmt

let msg = std_fmt.format("rx={} tx={}", rx_count, tx_count)?
send_log(msg)

This is the intended source API because building a string may allocate. It is not implemented end to end yet on the current toolchain.

Tip: Use use std/fmt as std_fmt to avoid shadowing core/fmt if you import both.

Format String Syntax

Format strings use a minimal placeholder language:

PlaceholderMeaning
{}Format next argument in default style
{{Literal {
}}Literal }

Placeholder count must match argument count exactly; a mismatch is a compile error:

fmt.write(out, "x={} y={}", x, y)      // ok
fmt.write(out, "x={}", x, y)           // error: too many arguments
fmt.write(out, "x={} y={}", x)         // error: too few arguments

Supported Argument Types

The current checked-format type checker accepts:

  • Unit ()
  • Integers (u8, u16, u32, usize, i8, i16, i32, isize)
  • bool: formats as true or false
  • f32: decimal representation
  • char: the character itself
  • string: the string value
  • Error: the error message

On the current executable path, the implemented argument slice is unit, booleans, integers, f32, char, string literals, and string locals. User-defined display/debug traits are not supported yet. For custom types, format the fields explicitly.

Backend Notes

Current backends may still use compiler-aware lowering behind fmt.write(...), but the sink contract is now the concrete core/io.Writer implementation selected for the first argument. Future targets may use a different backend strategy, including compact encoded records similar to defmt, behind the same source API.

Example: Status Line Over UART

use board/pico as board
use core/fmt
use std/time

fn main() -> never {
    let port = board.default_uart().init()
    let mut tick: u32 = 0

    loop {
        fmt.write(port, "[{}] alive\r\n", tick)
        tick += 1
        time.sleep_ms(1000)
    }
}