This macro accepts a ‘writer’, a format string, and a list of arguments. Arguments will be formatted according to the specified format string and the result will be passed to the writer. The writer may be any value with a write_fmt method; generally this comes from an implementation of either the fmt::Write or the io::Write trait. The macro returns whatever the write_fmt method returns; commonly a fmt::Result, or an io::Result.
Well, yes, it's not that obvious. It only stated that it would call write_fmt. And the exact behaviour would be up to destination type's write_fmt implementation, which in turns calls fmt::write by default. And if you peek into the source, you can see it's indeed writing piece by piece under default behavior. An overrided io::Write::write_fmt or fmt::Write::write_fmt implementation may have different result, but it's not the case in most sane situation.
I'm not really suggesting Rust it's using source code as a specification. But I think you can safely assume Rust is doing the "better" way if there's not significant drawback.
I do NOT want the intermediate format!(...) construction. I want write! to write in pieces, because I am writing lots of nested structs and I want to avoid every recursive call to construct an intermediate format!(...) string.
Another piece of evidence that it isn't allocating an intermediate string is that much of the formatting infrastructure (including the write! macro) is available in the core library, whereas allocation and the format! macro aren't included in core. The strings and arguments in the Arguments structure passed to write_fmt() are all provided via references.
Yes, and by the way, I don't think any standard library of any programming language have specifications for every single item it provides. Thus, for the foreseeable future, reading the source code is going to remain the best way to see how exactly something works.
Looking is fine. But it's not fine to then write code that relies on the implementation you saw.
The only things that are guaranteed are what's in the API contract. If you rely on something that's not in the contract a future Rust version may end up breaking your code.
The write! macro is in core and therefore cannot use a heap allocated buffer.
It might be however that format_args does some clever stack based optmizations. The builtin formatting machinery is not designed to be lightweight but for general purpose efficiency. If you need to minimize the formatting cost at runtime, f.i. because you are in an embedded context you might want to have a look at defmt.
In this particular case, I agree that one could try getting write! to guarantee the lack of internal buffering. But I wanted to note this is just barely within the boundary of reasonable things to document. At this level of nuances, it's quite possible that std::fmt might no longer be serving OP's use so well.
String formatting is mostly a solved problem, so in terms of stability, more and more guarantees can be made. However, a documentation can only hold so much details before it's no better than the source code. The requirements around code generation appear specific enough that these details might not always suffice.