I have already posted some pull requests around formatting. #126 Improve std.format.formatValue (-> already merged) #230 Issue 6448 - writef("%05d", BigInt) problem (-> already merged) #231 Issue 6595 - std.string.format() and sformat() are obsolete #235 Change toString signature taking sink #236 to!SomeString should use formatValue
After merging them, we can use const void toString(scope void delegate(const(char)[]) sink, ...) at all. If you need custom formatting with class/struct, you can define toString taking sink. class UserClass { string name; double value; // taking sink and formatStr version const void toString(scope void delegate(const(char)[]) sink, string formatStr) { formattedWrite(sink, "{%s %s}", name, value) } // taking sink and FormatSpec!char version, more efficiently than above const void toString(scope void delegate(const(char)[]) sink, FormatSpec!char f) { std.range.put(sink, '{'); std.range.put(sink, name); std.range.put(sink, ' '); formatValue(sink, value, f); // To through spec to 'value' field formatting, then support %s, %g, %a ... std.range.put(sink, '}'); } } And if you need heapfied formatting, you can write like follows: auto obj = new UserClass("name", 1.0); assert(std.conv.to!string(obj) == "{name 1.0}"); // used Appender + formatValue internally assert(std.string.format("%s", obj) == "{name 1.0}"); // ditto , and if you really need formatting into stack-allocated buffer: char[20] buf; char[] result = std.string.sformat(sink[], "%s", obj); // When buf.length is insufficient, FormatError is thrown assert(result == "{name, 1.0}"); Kenji Hara