Background
So I have lots of API that build up strings piece by piece. The easiest way to do it is to return a String. For example, url::form_urlencoded::serialize does this.
One issue with this approach appears when one wants to compose such functions. You might want to have, say, a form-urlencoded query string in the middle of an URL. Intermediate String return values each have their own memory allocation, which is wasteful.
So instead of a returning String, a function can take a &mut String parameter and append to it. I do this in e.g. url::percent_encoding::percent_encode_to.
Finally, what if the ultimate goal is not to obtain a single string in memory but write text to a file, or push it through a pipeline that processes it incrementally? The String::push_str method can be abstracted into a trait, and API generating text be made generic over implementations of that trait. This is similar to the std::io::Write trait, but with Unicode strings instead of bytes. Iāve made a TextWriter trait for this. (See it used in e.g. cssparser::serializer.)
std::fmt::Write
The formatting system (behind the format! macro and the Display trait) has exactly the same requirements, and the std::fmt::Write trait was introduced for the same reasons. It is almost identical to TextWriter, except that the documentation discourage its use:
This is similar to the standard libraryās io::Write trait, but it is only intended for use in libcore.
This trait should generally not be implemented by consumers of the standard library. The write! macro accepts an instance of io::Write, and the io::Write trait is favored over implementing this trait.
Proposals
Iād like to propose three changes. But the conventions here are more important than the actual code changes.
- āApproveā of using
std::fmt::Write other than for the formatting system by removing the quoted bits of documentation. It feels silly to maintain a trait almost identical to one in the standard library just because of a convention not to use the latter.
- To further show that they are not necessarily linked, move the trait out of
std::fmt. Iām not sure what module is a good destination, maybe std::str? Edit: Not std::io, I think. I/O is really bytes.
- Optional: Add a
write_char method to the trait, with a default implementation based on char::encode_utf8 and the write_str method. This is not strictly necessary (one can always use write!(w, "{}", c)), but I donāt see a reason not to have it. char is on of Rustās primitive types.
CC: @aturon, @alexcrichton.