kprint is a zero-overhead, stack-efficient logging and formatting crate designed for systems and embedded Rust code in the Zircon kernel environment.
It allows developers to write ergonomic Rust format strings ({}) that produce the same output as standard Rust formatting (core::fmt) while compiling invocations directly down to C printf / snprintf calls without intermediate stack buffers.
Standard Rust formatting (core::fmt::write!, format_args!, and println!) relies heavily on core::fmt::Formatter, trait objects, dynamic dispatch tables, and intermediate stack formatting buffers. In resource-constrained environments or bare-metal kernels, this introduces unwanted binary bloat and stack space overhead.
kprint solves this by translating Rust format strings and arguments at compile time into C printf / snprintf invocations that preserve Rust's formatting output semantics:
core::fmt) would produce, rather than performing a naive 1-to-1 syntax mapping to C printf specifiers. Where C printf differs from Rust (for example, C‘s %#x omits the 0x prefix when formatting 0 and %#X emits uppercase 0X, whereas Rust’s {:#x} and {:#X} always emit lowercase 0x (0x0, 0x2A), and strings default to left-alignment), kprint synthesizes the appropriate C format string and arguments (such as "0x%llx", "0x%06llX", "%*s%llx" with a computed prefix width, or "%-8.*s") so the resulting output matches Rust without allocating stack buffers."{:04x}" or "{:10s}") and converts them into static, null-terminated C format string literals (b"%04llx\0" or b"%-10.*s\0").AsKPrintStr, AsKPrintCString, AsKPrintSignedInt, AsKPrintUnsignedInt, AsKPrintPointer, AsKPrintChar, hex_alt_prefix_width).&str, &[u8], &CStr), kprint passes (slice.len(), slice.as_ptr()) to C %.*s specifiers, avoiding heap allocations or temporary string-copying buffers.printf calls (or snprintf for kformat!), avoiding core::fmt dispatch tables.use kprint::{kprint, kprintln}; fn example() { let score = 42; let user = "alice"; // Standard output without newline using captured variables kprint!("Connecting user {user:s} with ID {score:#06x}..."); // Standard output with newline kprintln!("Connection established."); }
kformat!)When you need to format into a stack buffer without allocating:
use kprint::kformat; fn format_example() { let mut buffer = [0u8; 128]; let result: &[u8] = kformat!(&mut buffer, "Status: {:#08X} ({})", 0x1A, true); assert_eq!(result, b"Status: 0x00001A (true)"); }
// Compile-time string concatenation kprintln!(concat!("prefix_", "status: {}"), 42); // Named and positional arguments kprintln!("{0} + {1} = {0}", "a", "b"); kprintln!("{greeting:s}, {name:s}!", greeting = "hello", name = "world"); // Null-terminated C string pointers (*const c_char) let c_ptr = c"kernel".as_ptr(); kprintln!("booting {:cs}", c_ptr);
The procedural macro infers C format families from literal types, cast expressions, or explicit format specifiers, translating them into C printf specifiers and arguments that match Rust's formatting behavior:
| Rust Specifier / Type | Generated C Specifier | C ABI Argument Cast / Trait | Example Output |
|---|---|---|---|
{}, {:d}, i32, isize | %lld | AsKPrintSignedInt (c_longlong) | -12345 |
{:u}, u32, usize | %llu | AsKPrintUnsignedInt (c_ulonglong) | 12345 |
{:x}, {:X} | %llx, %llX | AsKPrintUnsignedInt (c_ulonglong) | abcd, ABCD |
{:#x}, {:#X} | 0x%llx, 0x%llX | AsKPrintUnsignedInt (c_ulonglong) | 0x0, 0x2A |
{:#08X} | 0x%06llX | AsKPrintUnsignedInt (c_ulonglong) | 0x00001A |
{:<#10x} | 0x%-8llx | AsKPrintUnsignedInt (c_ulonglong) | 0x2a |
{:#8x}, {:>#8x} | %*s%llx | hex_alt_prefix_width, "0x", AsKPrintUnsignedInt | 0x1a, 0x0 |
{:p}, {:#p} | 0x%llx, 0x%016llx | AsKPrintPointer (*const c_void as c_ulonglong) | 0x0, 0x0000000012345678 |
{:o} | %llo | AsKPrintUnsignedInt (c_ulonglong) | 100 |
{:s}, &str, &[u8], &CStr | %.*s | AsKPrintStr (c_int, *const c_char) | fuchsia |
{:cs}, {:z}, *const c_char | %s | AsKPrintCString (*const c_char) | null-terminated |
{:c}, char, u8 | %.*s | AsKPrintChar (c_int, *const c_char) | Z, A, 🦀 |
{:b}, bool | %.*s | length (4 or 5), string pointer | true, false |
{:.2f}, f64 | %.2f | c_double | 3.14 |
core::fmtTo avoid Rust-side formatting loops and intermediate stack buffers, kprint delegates all formatting to C printf / snprintf and has the following intentional limitations:
{:s}) and characters ({:c}), field width and precision are measured in UTF-8 bytes (as understood by C printf's %.*s) rather than Unicode scalar values (chars).%lld) unless an explicit type specifier (:s, :u, :x, :X, :p, :c, :b, :f, :e, :E, :cs) is provided.'0' padding with left (<) or right (>) alignment are supported by C printf. Specifying a custom fill character (such as {:_>8}) or center alignment ({:^8}) produces a compile-time error.c_longlong / c_ulonglong (64-bit).printf Delegation for :o, :f, :e, :E, and :b:{:b} formats bool values ("true" / "false") rather than base-2 integers.{:o}, {:f}, {:e}, and {:E} map directly to C printf‘s %llo, %f, %e, and %E (e.g., {:.1e} formats the exponent using C’s 1.0e+03 notation).