How to get written bytes count for formatted string?

Here a simple code snippet:

fn main() {
    use std::io::Write as _;
    let mut s = [0_u8; 64];
    s.as_mut().write_fmt(format_args!("{}", 12345)).unwrap();
    let n = 3; // need to actually calculate written bytes count
    let s = std::str::from_utf8(&s[..n]).unwrap();
    println!("{}", s);
}

So I'd like to preallocate buffer, then write there some bytes and get amount of bytes written. In this exact case I could just scan trailing zero bytes and detect when they end but It's just so ugly. The write function returns exactly what I need but it doesn't accept any kind of formatting but &[u8] instead.

What do I do is call Display but to preallocated stack buffer not string like in ToString.
What are my options to do this in the cleanest way?

No, not really. \0 is valid UTF-8; Rust strings can contain embedded zeros without problem, so finding the first zero won't give you the correct result.

Apart from this problem, I don't think what you want is fully possible. Even if you compute the number of bytes, you won't be able to allocate a dynamically-sized buffer on the stack.

Anyway, there's an easy way to perform the calculation: just ask the fmt::Write how many bytes it was told to write! For that, you can do something like

struct WriteCounter(usize);

impl fmt::Write for WriteCounter {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.0 += s.len();
        Ok(())
    }
}
3 Likes

The std::io::Cursor type allows the above without needing a custom struct, since you can just query its position after the write.

That doesn't quite work though: it requires a big enough buffer to be already allocated.

You can use arrayvec crate. It's de-facto standard solution for fixed-capacity buf dynamic-length buffers and even no_std compatible.

fn main() {
    use std::fmt::Write  as _;
    use arrayvec::ArrayString;

    let mut s = ArrayString::<64>::new();
    write!(s, "{}", 12345).unwrap();
    println!("{}", s);
}
1 Like

Okay then, I thought it's something I can do without relying on extra crates.

Thanks

But that doesn't work for dynamically sizing the buffer either, does it?