-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add fmt size_hints #583
Add fmt size_hints #583
Conversation
I'm all for adding a hint for formatting. It would be nice in this RFC to mention unifying this with iterator size hints. cc-ing @kballard and @thestinger, who I believe collaborated on adding Does anyone know of anything using the #46 is somewhat related to this RFC. |
I don't know offhand of anyone using the All that said, if the |
A drawback to this proposal: all manual implementations of Something to consider: A macro like |
SizeHint { | ||
min: self.min + other.min, | ||
max: match (self.max, other.max) { | ||
(Some(left), Some(right)) => Some(left + right), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation doesn't handle overflow. It needs to look something like
SizeHint {
min: self.min.saturating_add(other.min),
max: if let (Some(left), Some(right)) = (self.max, other.max) {
Some(left.checked_add(right))
} else {
None
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, will adjust.
Offhand, I am in favor of making formatting strings more efficient, especially if that makes |
That's exactly what I had thought with the range: if the range is large, it I looked at Iters size_hint for design, but they're a little different. @kballard perhaps debug_asserts could be added to ensure at the end that On Wed, Jan 14, 2015, 11:12 AM Kevin Ballard [email protected]
|
} | ||
``` | ||
|
||
Add a `SizeHint` type, with named properties, instead of using tuple indexing. Include an `Add` implementation for `SizeHint`, so they can be easily added together from nested properties. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an interesting deviation from the size hints of iterators. I would personally expect the two to have the same signature, and this may want to at least discuss the discrepancy between the two return values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It started because I wanted to implement Add, and it felt odd to impl on such a generic tuple.
I've got a different approach that does not change the API of With this approach I'm seeing similar numbers for your
|
@kballard @alexcrichton I've updated the RFC with your comments. |
Please benchmark number formatting. rust-lang/rust#19218 |
value: &'a Void, | ||
formatter: fn(&Void, &mut Formatter) -> Result, | ||
hint: fn(&Void) -> SizeHint, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be worth profiling the impact of this representation as it's inflating the size from 2 words to 3 words. Formatting is normally not necessarily on hot code paths, but it does affect code size and stack size and we've had to optimize it in the past. In theory an Argument
is just a trait object so the formatter/hint pair could point to a "vtable" which could just be constructed manually instead of as a trait object itself.
Regardless though, it's a pretty minor point, just something to think about :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about this as well. Currently, any function can be passed here, but if we didn't want that flexibility, we could create an enum Format { Show, String, LowerHex, ... }
and change Argument to be struct Argument { formatter: Format, value: &Void }
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At some point we do need to force rustc to monomorphize an implementation of Show
as how would you actually call the fmt
function given just formatter
and value
? (e.g. a function pointer needs to be somewhere)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagined being able to look them up from the enum:
impl Format {
fn get_fns(&self) -> (fn(&Void, &mut Formatter) -> Result, fn(&Void) -> SizeHint) {
match *self {
Format::Show => (Show::fmt, Show::size_hint),
// ...
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll need type inference to drive inference for the Self
type of Show::fmt
:
use std::fmt::Show;
fn main() {
let f = Show::fmt;
}
foo.rs:4:13: 4:22 error: type annotations required: cannot resolve `_ : core::fmt::Show`
foo.rs:4 let f = Show::fmt;
^~~~~~~~~
foo.rs:4:13: 4:22 note: required by `core::fmt::Show::fmt`
foo.rs:4 let f = Show::fmt;
^~~~~~~~~
error: aborting due to previous error
Thanks for the additions @seanmonstar! Could you also add a part explicitly saying that everything will be |
I've also wondered if we could just add length hinting methods to |
@reem By the time you can call that, the buffer has already been allocated. And if the object has properties that are also going to be formatted, those properties can't tell the Formatter their hint until part way through the formatting. |
@alexcrichton I added unstable attributes to the struct and method. |
```rust | ||
impl<T: fmt::String> ToString for T { | ||
fn to_string(&self) -> String { | ||
format!("{}", self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started implementing this, and ran into that the format macro is defined in libstd, and this trait is in libcollections. The reason it's in std is because it needs String to use as a buffer.
What if I moved this macro lib collections, and had it call String::format instead of std::fmt::format?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to define the format!
macro in libcollections and then use macro_reexport
in the standard library (like it does for vec! already)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is exactly my plan, c.f. rust-lang/rust#21912 and #810.
Though really, I don't think either of these modifications really needs to block this RFC. I'll try to ping @alexcrichton about getting it in front of the core team. |
@pnkfelix I added the drawback. Regarding numbers, I imagine a minor improvement, since this reduces the number of allocations for the buffer. However, it seems the slowness in formatting numbers has more to do with that implementation, no? |
@seanmonstar yeah probably |
ping @alexcrichton |
Hm I've been thinking about this recently, and I'm somewhat worried about the code size implications here. For example the With formatting, however, trait objects are its bread and butter, so we're forced to keep all
I don't think that this will turn up any showstoppers, but I'd like to have a handle on what we're getting into. Otherwise I think that this is basically good to go so I'll see what we can do to get it merged soon afterwards. |
The RFC's current If there are format parameters, then the I wonder if I'm inclined to think the size hint should be |
I tried to sum size hints in the expansion of |
ping @alexcrichton |
@seanmonstar do you have any updates on the impact on code size here? |
I do not. The branch I started is several months old, and I haven't had time to rebase. @rprichard raises some points worth considering, though. |
Note: this RFC entered its Final Comment Period as of Yesterday; 6 days remain before a final decision will be made. |
Don't make the decision until the discussion about |
Accepting this RFC does not require setting the name in stone. Though we all love to bikeshed names, I think we can all agree they're not really the most important part of this proposal. Implementing this of course takes time, and landing it in stable even more. |
Another alternative would be instead of including this method, for fmt to use a thread-local LruCache of sizes when formatting types. This would result in the first case not getting the speed benefit, but would mean that all implementations of a fmt trait can use a size hint, without requiring the |
Having now gone through the final comment period, it's time to make a decision on this RFC. For now I'm going to close this RFC for the following reasons:
Overall it seems best to start this RFC fresh again with a new look beyond microbenchmarks to the statistics measured here, and also perhaps await the outcome of #1034. Thanks regardless for the RFC @seanmonstar! |
like gankro said, the name is a non-issue. |
Add a
size_hint
method to each of thefmt
traits, allowing a buffer to allocate with the correct size before writing.Rendered
cc @alexcrichton