&mut [u8] and core::fmt::Write

So, in std, there's this quite elegant pattern that's available to format into a byte buffer:

use std::io::Write;
fn toast() {
    let mut buf = [0u8, 10];
    let mut slice = &mut buf[..];
    let integer = 0;

    write!(slice, "{integer}").expect("...");
}

This isn't possible to do without std and std::io::Write, despite there being an equivalent in core::fmt::Write.

This was quite suprising to me, you can workaround it by doing an extension trait on &mut [u8], but this seems a bit unnecessary.

Is there a reason &mut [u8] doesn't implement core::fmt::Write ?

While you can use core::fmt::Write on byte slices, it is fundamentally for strings (it will also reject non-valid Unicode AFAIK). std::io::Write is for byte slices.

If we had a stack-string (ArrayString in arrayvec - Rust) in std, it would probably implement core::fmt::Write. But we don't.

I guess the "correct" API for this sort of thing would be to write into an &mut [u8] or even &mut [MaybeUninit<u8>], producing an &mut str (reborrowed from the &mut [u8]) that referred to just the written potion – that way you have a reference that knows it's pointing to UTF-8, pointing at the existing storage. But that isn't very compatible with the way that the Write traits work today.

The problem with doing an API like the one suggested in the original post is that it loses information about how much of the input has been written (and thus how much is valid UTF-8).