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
(like a
[String
]) or io::Write
(like
io::stdout()
), 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.
Start by adding the unstable feature:
```rust
```
Now, add your function signature:
```rust use cty::{cchar, cint};
unsafe extern "C" fn clibraryprint(str: *const cchar, mut args: ...) -> cint { 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:
rust
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