`NumBuffer<T>` ties a buffer to one integer type; no reusable buffer for mixed-integer formatting
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
The std-replacement-data PR rust-lang/std-replacement-data#19 flagged core::fmt::NumBuffer as the std replacement for itoa. I tried to migrate off itoa and couldn't: the current API doesn't let me reuse one buffer across integer types.
NumBuffer<T> is generic over the concrete integer type, and format_into takes &mut NumBuffer<Self>, so a buffer is bound to exactly one integer type. Code that formats several integer types has to hold a separate buffer per type, and can't hide that behind a single generic helper, since the caller still has to supply the matching NumBuffer<T>.
itoa (and zmij for floats) don't have this: Buffer is a single non-generic type and format is generic over the value, so one buffer instance serves every integer (or float) type.
I hit this in a printf implementation. With itoa, one buffer covers every integer the format string throws at it:
use core::ffi::{c_char, c_double, c_int, c_long, c_uint};
// parsing/writing omitted for brevity
unsafe fn printf_internal(mut args: va_list) {
let mut ints = itoa::Buffer::new(); // one buffer, every integer type
let mut floats = zmij::Buffer::new(); // one buffer, every float type
let buf: &[u8] = match spec {
b'd' | b'i' => ints.format(unsafe { args.next_arg::<c_int>() }).as_bytes(),
b'u' => ints.format(unsafe { args.next_arg::<c_uint>() }).as_bytes(),
b'l' => ints.format(unsafe { args.next_arg::<c_long>() }).as_bytes(),
b'f' => floats.format(unsafe { args.next_arg::<c_double>() }).as_bytes(),
b's' => unsafe { CStr::from_ptr(args.next_arg::<*const c_char>()) }.to_bytes(),
_ => todo!(),
};
}
The NumBuffer translation needs one buffer per integer type, and the list grows with every width/signedness a real printf supports (%lld, %zu, %jd, %td, ...):
use core::ffi::{c_char, c_double, c_int, c_long, c_uint};
use core::fmt::NumBuffer;
unsafe fn printf_internal(mut args: va_list) {
let mut floats = zmij::Buffer::new(); // NumBuffer is integers only
let mut i = NumBuffer::<c_int>::new();
let mut u = NumBuffer::<c_uint>::new();
let mut l = NumBuffer::<c_long>::new();
// + one NumBuffer per additional type, up to 10 to cover all widths
let buf: &[u8] = match spec {
b'd' | b'i' => unsafe { args.next_arg::<c_int>() }.format_into(&mut i).as_bytes(),
b'u' => unsafe { args.next_arg::<c_uint>() }.format_into(&mut u).as_bytes(),
b'l' => unsafe { args.next_arg::<c_long>() }.format_into(&mut l).as_bytes(),
b'f' => floats.format(unsafe { args.next_arg::<c_double>() }).as_bytes(),
b's' => unsafe { CStr::from_ptr(args.next_arg::<*const c_char>()) }.to_bytes(),
_ => todo!(),
};
}
What I'd like
Some sanctioned way to drive one reusable buffer for every integer type, matching itoa's Buffer::format.
#143636 attempted this by making format_into generic (one NumBuffer<u128> backing any narrower integer). From what I can tell that was rejected, at least in that form: the suggestion was instead a method on NumBuffer that converts it to a NumBuffer of a different type, gated by trait bounds, and tracked separately from the rest of int_format_into. This issue could be that follow-up. I don't have a preference on the mechanism, I just need one buffer to serve multiple integer types without a distinct binding per type.
Refs: #138215 (tracking issue), #143636 (rejected generic format_into attempt).
cc @GuillaumeGomez
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with core::fmt::NumBuffer and its format_into API, then read tracking issue #138215 and rejected attempt #143636 for the prior design constraints. Determine a sanctioned API that lets one buffer serve multiple integer types without separate bindings. Done means mixed-integer formatting can reuse one buffer, matching the requested itoa::Buffer behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100