printf
reimplemented in Rust
This is a complete reimplementation of printf
in Rust, using the unstable
(i.e. requires a Nightly compiler) c_variadic
feature.
printf
, use this crate to easily add
your own output. [core::fmt
] too big? No problem! Write your own
formatting code, or use a minimal formatting library like ufmt
or
defmt
. Don't need every single option given by printf
format
strings? No problem! Just don't implement it.wasm32-unknown-unknown
instead of emscripten
(as wasm-bindgen is only compatible with the former), you have no libc. If
you want to interface with a C library, you'll have to do it all yourself.
With this crate, that turns into 5 lines instead of hundreds for printf
.printf-compat lets you pick how you want to output a message. Use
pre-written adapters for [fmt::Write
]output::fmt_write or [io::Write
]output::io_write, or implement your own.
This crate is no_std
compatible (printf-compat = { version = "0.1",
default-features = false }
in your Cargo.toml). The main machinery doesn't
require the use of [core::fmt
], and it can't panic.
Of course, printf
is completely unsafe, as it requires the use of
va_list
. However, outside of that, all of the actual string parsing is
written in completely safe Rust. No buffer overflow attacks!
The n
format specifier, which writes to a user-provided pointer, is
considered a serious security vulnerability if a user-provided string is
ever passed to printf
. It is supported by this crate; however, it
doesn't do anything by default, and you'll have to explicitly do the writing
yourself.
A wide test suite is used to ensure that many different possibilities are
identical to glibc's printf
. [Differences are documented][output::fmt_write#differences].
Start by adding the unstable feature:
#![feature(c_variadic)]
Now, add your function signature:
use cty::{c_char, c_int};
#[no_mangle]
unsafe extern "C" fn c_library_print(str: *const c_char, mut args: ...) -> c_int {
todo!()
}
If you have access to [std
], i.e. not an embedded platform, you can use
[std::os::raw
] instead of [cty
]. Also, think about what you're doing:
printf
because you don't have one, you'll want to
call it printf
and add #[no_mangle]
.#[no_mangle]
and
rename the function to what it expects.#[no_mangle]
.Now, add your logic:
#
use printf_compat::{format, output};
let mut s = String::new();
let bytes_written = format(str, args.as_va_list(), output::fmt_write(&mut s));
println!("{}", s);
bytes_written
Of course, replace [output::fmt_write
] with whatever you like—some are
provided for you in [output
]. If you'd like to write your own, follow
their function signature: you need to provide a function to [format()
]
that takes an [Argument
] and returns the number of bytes written (although
you don't need to if your C library doesn't use it) or -1 if there was an
error.
License: MIT OR Apache-2.0