Why doesn't Vec<u8> implement std::fmt::Write?


#1

The std::fmt::Write trait is implemented by String but not Vec<u8>. Why? A string is a constrained slice of bytes, so anything written to a String could also be written to a Vec<u8>.

Could this be added to the standard library?


#2

I think it’s because std::fmt::Write is for writing to the target with the intention of displaying the data. Vec<u8> is not for displaying as such; which is why std::io::Write is implemented for Vec<u8>.


#3

std::io::Write works, and it can format things via write! macro:

use std::io::Write;

fn main() {
   let mut buf = Vec::new();
   write!(&mut buf, "{}", 1);
}


#4

Thank you for the replies.

In my case, I have a generic function that I use both for implementing Debug and Display, as well as for other cases where it generates a UTF-8 subset of non-UTF-8 binary data residing in a Vec<u8>. Something like:

pub fn helper<W: std::fmt::Write>(
    writer: &mut W,
    text: &[u8],
) -> Result<(), std::fmt::Error> {

I could change helper to instead accept std::io::Write, but that would require the Debug and Display implementations to buffer their output to a temporary instead of writing it directly to the Formatter (because Formatter does not (and cannot) implement std::io::Write). I would prefer to avoid such buffering—not to mention losing the UTF-8 guarantee by going from std::fmt::Write to std::io::Write.

Alternatively, I could replace helper with a macro, which would then support std::io::Write/std::fmt::Write duck-typing via write!, but I would prefer to have it as a function instead of a macro. The best solution would be to have Vec<u8> implement std::fmt::Write—if that’s possible.


#5

Your suggestion sounds reasonable.

You can bridge this with a wrapper type, that’s another way. (playground link)

use std::fmt::{self, Write};
use std::io;

pub struct ToWriteFmt<T>(pub T);

impl<T> fmt::Write for ToWriteFmt<T> where T: io::Write
{
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.0.write_all(s.as_bytes()).map_err(|_| fmt::Error)
    }
}

#6

@bluss, that’s a good idea, thanks!