-
Notifications
You must be signed in to change notification settings - Fork 20
ACP Implement fmt::Write
wrapper for &mut [u8]
(and maybe &mut str
?)
#278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Does I'd be interested in getting something like ArrayVec and ArrayString into |
I think First, an Second, this feature would be closer to |
I put together a draft implementation: rust-lang/rust#117931 It's not large (~120 lines including docs), and enables formatting data into a |
Can we re-use |
Implementing You could use a |
What do you mean? We can get the written portion via |
That's not deconstruction, it's a reborrow. |
We can add a method for deconstruction if it's a common need (if not, it can be emulated by taking the |
I think the ACP covers what I'm about to say, but just to clarify: I know a Consider C, which has the #include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
void fmt_u16(char *buf, size_t buf_len, uint16_t v) {
assert(buf_len >= 6);
sprintf(buf, "%hu", v);
}
int main() {
char buf[6];
fmt_u16(buf, sizeof buf, 12345);
printf("formatted: %s\n", buf);
return 0;
} The equivalent in Rust would be written like this in current stable (lifetimes explicit for clarity): use core::fmt;
use core::mem::MaybeUninit;
fn fmt_u16<'a>(buf: &'a mut [MaybeUninit<u8>], v: u16) -> &'a str {
struct W<'a> { buf: &'a mut [MaybeUninit<u8>], idx: usize }
impl fmt::Write for W<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.buf[self.idx..self.idx+s.len()].copy_from_slice(unsafe {
core::mem::transmute(s.as_bytes())
});
self.idx += s.len();
Ok(())
}
}
let mut w = W { buf, idx: 0 };
let _ = fmt::Write::write_fmt(&mut w, format_args!("{v}"));
unsafe {
let utf8: &[u8] = core::mem::transmute(&w.buf[..w.idx]);
core::str::from_utf8_unchecked(utf8)
}
}
fn main() {
let mut buf = [MaybeUninit::uninit(); 6];
println!("formatted: {:?}", fmt_u16(&mut buf, 12345));
} And it would be really nice if all that extra stuff wasn't needed -- if non-allocating format look like this: fn fmt_u16<'a>(buf: &'a mut [MaybeUninit<u8>], v: u16) -> &'a str {
let mut cursor = fmt::WriteCursor::new_uninit(buf);
write!(cursor, "{v}");
cursor.into_str()
}
fn main() {
let mut buf = [MaybeUninit::uninit(); 6];
println!("formatted: {:?}", fmt_u16(&mut buf, 12345));
} |
I just put out a crate that tries to solve the composition problem in a generalized fashion: https://github.com/SUPERCILEX/io-adapters Example usage that (I believe) solves this proposal's use case: //! ```cargo
//! [dependencies]
//! io-adapters = "0.1"
//! ```
#![feature(core_io_borrowed_buf)] // Or just use a normal array if you want to keep things simple
use core::fmt;
use std::{io::BorrowedBuf, mem::MaybeUninit, str::from_utf8};
use io_adapters::WriteExtension;
fn main() {
let mut buf = [MaybeUninit::uninit(); 128];
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
foo(buf.unfilled().write_adapter());
println!("{:?}", from_utf8(buf.filled()));
}
fn foo(mut output: impl fmt::Write) {
writeln!(output, "Hello, World!").unwrap();
} |
Proposal
Problem statement
The ability to place formatted text into a buffer seems like a useful primitive to have, so I propose adding types to
core::fmt
that implementWrite
on top of a user-provided&mut [u8]
. These types would provide a safe way to access the portion of the buffer that has been written to.Additional variants that seem useful to add at the same time:
&mut str
that usesstr::as_bytes_mut()
, with careful attention toward handling the EOF case. This would let the user avoid UTF8 revalidation.&mut [MaybeUnint<u8>]
would involve a bit more setup boilerplate but enable bypassing the cost of buffer initialization.Motivating examples or use cases
I frequently find myself writing a
BufWriter { &mut [u8] }
wrapper struct inno_std
code when I want to do the Rust equivalent ofsnprintf()
, for example when constructing Linuxmount()
data options in rust-fuse: https://github.com/jmillikin/rust-fuse/blob/47ca3bc5cda263543cf6a71155115a85dd1d094e/fuse/os/linux.rs#L539-L560.Solution sketch
Something roughly like this -- I'm not attached to the names or the exact API, and the real implementation would leverage unsafe-unchecked methods to avoid bounds checks.
Alternatives
Add a function like
write_to(buf: &mut [u8], w: &impl Write) -> Result<usize, fmt::Error>
tocore::fmt
.Add a type modeled on
std::io::Cursor
, which implements additional methods for reading and seeking.core::io::Cursor
isn't possible because that would cause name conflicts withstd::io::Cursor
, andcore::fmt
would be a weird place to put a type that does more than formatting.std::io::Cursor
?Links and related work
This pattern is a popular StackOverflow question:
There's another open proposal for
fmt::Write
that seems related:std::io::Write
.What happens now?
This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
Second, if there's a concrete solution:
The text was updated successfully, but these errors were encountered: