I recently looked into redesigning fmt!, and I wanted to post my ideas before
going all the way to ensure that I'm not missing out on anything.
== Today's state ==
As of today, fmt! works pretty well. It's a very useful function and will likely
be a very common thing in all rust code using libstd. It does have a few
drawbacks though:
1. It always returns ~str. This should not be the case, rather it should always
write into some form of stream (io::Writer/io::rt::Writer). This avoids any
unnecessary allocations.
2. If you're formatting via %X, then the value must have one, and only one type
(you can't implement %s for your own types).
3. Format specifiers are a predefined compiler constant. It would be pretty
awesome if libraries/programs could specify their own fmt! specifiers via
something like a #[fmt="name"] attribute
4. Currently fmt! results in a lot of code at the callsite. This is because the
string allocation and building the string are all implemented inline. Only
the actual conversions to strings are performed in function calls.
5. It's generally slow right now due to performing large numbers of intermediate
allocations. A printing function should perform 0 allocations.
There are probably other limitations, but those are the big ones I can think of.
== What I'd like to see ==
I'm envisioning a rustc that predefines two macros, fmt! and fprintf! (maybe
ffmt!). The former would be as it is today, returning ~str, and the latter would
take a stream instance to write information to. Then fmt! would be implemented
with a memory buffer to fprintf!. This would solve problem 1 (kinda, more down
below).
Furthermore, the codegen would be different. Format strings would still be
parsed at compile time, but the format string would be passed through instead of
passing around these Conv structures at runtime. This means that the actual
function will then take a string and a slice of values to print (the types of
the values are verified at compile-time still). This would solve problem 2.
All printing will be done through traits. For example:
#[fmt="d"]
trait Signed {
// Emit to Formatter's stream using its parameters for
width/precision/etc.
fn fmt(&Self, &mut std::fmt::Formatter);
}
Anything which implements this trait can then be used with the '%d' format
specifier. Furthermore this means that you can create user-defined format
specifiers (via the fmt attribute). I would imaging that multi-char format
specifiers could be something like: "%(mytype)". This solves problems 3 and 4.
I'd also do my best at the same time to remove any and all allocations, but it's
not guaranteed that the system today can do that. Right now I think that there's
limitations blocking this.
== Is this possible? ==
In my opinion, the way to go about this is not to use trait parameterization,
but rather trait objects. In order to avoid allocations, the functions should
also take &Trait. From what I can tell, however, this is fairly broken and most
operations don't work at all. Plus I don't think there's any coercion from
~Trait and @Trait to &Trait yet.
Regardless, for now I don't think that fprintf! can exist, but I do believe that
I can rewrite fmt! to use this new system of defining formats. I can also try to
open up specific bugs about &Traits to get them to a working conditions.
===
Does this sound like a sane replacement for fmt? All code today would
work the same as it used to, but in theory it would be a bit faster
and hopefully more powerful as well.
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev