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 ofio::Write
, and theio::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, maybestd::str
? Edit: Notstd::io
, I think. I/O is really bytes. - Optional: Add a
write_char
method to the trait, with a default implementation based onchar::encode_utf8
and thewrite_str
method. This is not strictly necessary (one can always usewrite!(w, "{}", c)
), but I donāt see a reason not to have it.char
is on of Rustās primitive types.
CC: @aturon, @alexcrichton.