lwprintf-rs
Lightweight printf bindings for Rust, powered by the upstream C library lwprintf. Provides no_std support and minimal glue to hook custom output sinks from Rust.
Overview
- Builds the upstream C source via
ccand generates bindings viabindgenat compile time. - Exposes the core C APIs (
lwprintf_printf_ex,lwprintf_vprintf_ex,lwprintf_snprintf_ex,lwprintf_vsnprintf_ex) plus Rust helpers and macros. - Lets you plug in a Rust-defined output sink through the
CustomOutPuttrait andLwprintfObjwrapper.
Public API surface (Rust crate)
- Trait:
CustomOutPut { fn putch(ch: i32) -> i32; }– implement to handle each output byte/char. - Struct:
LwprintfObj<T: CustomOutPut>new()– create an instance (uninitialized).as_mut_ptr()– get*mut lwprintf_tfor calling the raw C FFI.
- Functions (re-exported raw C FFI):
lwprintf_printf_ex,lwprintf_vprintf_exlwprintf_snprintf_ex,lwprintf_vsnprintf_ex
- Convenience wrappers (Rust-side varargs pass a
VaListto the raw APIs):lwprintf_vprintf_ex_rust,lwprintf_vsnprintf_ex_rust
- Macros (default instance = null
lwobj):lwprintf_printf!,lwprintf_vprintf!lwprintf_snprintf!,lwprintf_vsnprintf!lwprintf_sprintf!,lwprintf_sprintf_ex!
Example usage
cargo run --example print
Quick start
use ;
;
Notes on varargs
- Rust cannot safely re-forward C varargs; use the raw FFI exports for formatting (
lwprintf_printf_exetc.) and pass the pointer fromas_mut_ptr(). - For
va_list-style calls, preferlwprintf_vprintf_ex/lwprintf_vsnprintf_exor the Rust helperslwprintf_vprintf_ex_rust/lwprintf_vsnprintf_ex_rustwhich forward aVaListto C.
Build
- The build script compiles
lwprintf.cand generates bindings on the fly. No pre-generated bindings are checked in. - Ensure
clangand a C toolchain are available for bindgen/cc.
no_std support
- This crate can be used in
no_stdenvironments, but requires a musl toolchain. When compiling the lwprintf C code, musl's gcc is needed to set the correct sysroot and handle missing headers. - Supported architectures and their corresponding musl toolchains:
- loongarch64: loongarch64-linux-musl-gcc
- x86_64-linux-musl-gcc
- riscv64-linux-musl-gcc
- aarch64-linux-musl-gcc
Why varargs forwarding is tricky
Rust cannot preserve the original C varargs ABI layout when re-forwarding ... across Rust functions. A Rust wrapper that takes ... and then forwards to another ... function will corrupt the call frame. Always call the raw C varargs functions directly (or use va_list variants) once arguments are marshalled.