Uh oh!
There was an error while loading. Please reload this page.
RFC: fmt::Show and fmt::String guidelines - #565
Conversation
wycats
commented
Jan 9, 2015
I think it's worth elaborating on the problem of nesting one type's "for human consumption" into a generic type's "for human consumption". Imagine that cargo::Package implements a The person who implemented |
wycats
commented
Jan 9, 2015
@aturon I'm struggling with how to decide when type information can be left off of a Can you suggest some straw-man guidelines that differentiate between I'd like to flesh that out and get it added to the RFC 😄 |
Diggsey
commented
Jan 9, 2015
Is it possible to only show type information for the top level? The only exception would be when you have dynamic polymorphism, in which case you'd need to output the specific type, eg. |
dgrunwald
commented
Jan 9, 2015
Why? In that light, I'd say integer suffixes can be left off because the compiler can almost certainly infer them when building the unit test. Also, how do we |
wycats
commented
Jan 9, 2015
@Diggsey Not without multiple traits (or an additional But perhaps more importantly, I think it's clear that |
wycats
commented
Jan 9, 2015
In Ruby: In Node: @aturon I don't see a clear way to implement this with the existing |
aturon
commented
Jan 9, 2015
@wycats Yeah, I'm also uncomfortable with how vague the initial proposal is here, and hoping that we can iterate toward something crisper. One problem is to balance uniformity against readability. It's hard to see how to avoid special cases. For example, the outcome I'd like for
Those are some ad hoc rules! But I think there is a clear intent behind them: to make it easy to make sense of compound data of various kinds without overwhelming debugging output with every last bit of type information. Essentially, types at the leaves are left off, as are types of "trivial" wrappers (like smart pointers), but composed things like Compare: Vec{MyStruct{ f1:0u32,f2:"some\nstring",f3:MyNullaryVariant},MyStruct{f1:1u32,f2:"",f3:MyUnaryVariant(0.3f32)}}to [MyStruct{f1:0,f2:"some\nstring",f3:MyNullaryVariant},MyStruct{f1:1,f2:"",f3:MyUnaryVariant(0.3)}]I feel like the second version is much less noisy, but hasn't lost any interesting information. I would love some help making this more crisp, if others share similar preferences. |
Diggsey
commented
Jan 9, 2015
@wycats Well one way would be to have the Show trait always omit the type if known statically, and then have a shortcut way to "show" both a value and it's compile-time known type. Also, I don't think your example is necessarily true, I'd prefer this: Than this (especially for longer lists, and doubly for when the type is more complex, eg. Person< Some type params... > or something): |
wycats
commented
Jan 9, 2015
@aturon quick clarification: When you say "String" there, you mean every kind of String ( |
wycats
commented
Jan 9, 2015
@Diggsey can you show a sample implementation of a sample |
wycats
commented
Jan 9, 2015
@aturon you mean at the top-level only, right? |
wycats
commented
Jan 9, 2015
Unless you're debugging an overflow or precision issue ;) |
aturon
commented
Jan 9, 2015
Yes.
No, I mean at any level. See the example -- Given that we want And anyway, for nested structures I think it's still useful to see the struct name: Foo{f1:Bar{b1:0,b2:"hello"},f2:Baz(3)} |
aturon
commented
Jan 9, 2015
My point is just that this information is readily available elsewhere, especially if we print the type name for any enclosing structs. |
wycats
commented
Jan 9, 2015
I mean that the CONTENTS of a struct are not printed as you would normally construct them. Only the structure. |
wycats
commented
Jan 9, 2015
And my point is that sometimes you're poring through tons of trace logs and going back to the original definition is very time consuming if you actually care. |
dgrunwald
commented
Jan 9, 2015
@wycats: I just looked at the python implementation, it simply maintains a list of containers that currently have an active |
wycats
commented
Jan 9, 2015
@dgrunwald that seems... not ideal for a Rust |
aturon
commented
Jan 9, 2015
I'm afraid I still don't understand. The contents of the structure are printed according to whatever their
Maybe I'm way off base, but it seems like if we're printing the surrounding type information -- which struct and field is this going into -- it should be very very easy to discover the type information. That's the sense in which it seems highly redundant to me. I suppose in some cases being reminded of the type information in debug output could help you make a connection you wouldn't otherwise? Is that what you have in mind? |
dgrunwald
commented
Jan 9, 2015
@wycats: The other option would be to change the trait to pass around a |
aturon
commented
Jan 9, 2015
I should have laid this out more explicitly, but while I can see the benefit of what you're suggesting, I worry that it would lead to much more verbose debugging output in general. Further, in a lot of cases in practice it's not really possible to do this, because the internals of a data type are private; at the very least, the debugging output might involve a call to a constructor, etc. That said, I think in practice the conventions I'm suggesting will put you in the ballpark; I mostly wanted to clarify that it's not a hard-and-fast rule.
Agreed. |
Diggsey
commented
Jan 9, 2015
@wycats Types would implement Show such that they'd never output type information, and to actually display a value when debugging it would do this: Although obviously it would be nicer if there was a better syntax (maybe there could be a format specifier which automatically appended the type information or something) |
aturon
commented
Jan 9, 2015
cc @seanmonstar |
aturon
commented
Jan 9, 2015
cc @nick29581 |
seanmonstar
commented
Jan 10, 2015
Regarding not printing integer suffixes: I see the pain. But I also have cases where I want to see them. Take this simple example: fndo_things<T:Show>(t:T){debug!("doing things with {:?}", t);}do_things(1is);do_things(1u64);do_things((1u8,2u8));In this case, I'd need to be able to see what I got, since I don't have the actual type definition right there. But I definitely see the grossness in printing it for a big vector. Could we make use of the alternate If I had my way, I'd go down the route of making everything much noisier in debug outputs. debug!("{:?}","foo");// &"foo"debug!("{:?}","foo".to_string());// String { "foo" }// etc |
Screwtapello
commented
Jan 10, 2015
However, Rust source is a notation that's good for concisely and accurately describing Rust values, and one that Rust programmers are likely to be familiar with. Even if a |
jpernst
commented
Jan 22, 2015
Other than Since this takes self by value, the original struct would be lost if the function fails, hence it is returned in the tuple of the "error" value so you can recover it. Another possibility is to add the original object as a parameter of the error object, but this isn't ideal since moving the original object out of the error would render the error unusable after the move, preventing you from retaining it for logging purposes or propagating it up the stack. Further, the original object has lifetime constraints as well as a destructor, so it would restrict the life of the error object unnecessarily. In short, I agree that the error value of |
nikomatsakis
commented
Jan 22, 2015
(Note though that this requirement is only imposed for |
seanmonstar
commented
Jan 22, 2015
I find I only use unwrap to ease writing tests. Having to impl Display as On Thu, Jan 22, 2015, 1:21 PM Niko Matsakis notifications@github.com
|
aturon
commented
Jan 22, 2015
FWIW, I'm finding the arguments in favor of However, I think @alexcrichton may have some further thoughts. |
jsanders
commented
Jan 22, 2015
I think it would be nice to have all three of |
carllerche
commented
Jan 22, 2015
@aturon I think that w/ specialization, there can be a default |
alexcrichton
commented
Jan 22, 2015
After reading over these comments I reached the same conclusion as @carllerche, which I believe for now sounds like we should to back to |
aturon
commented
Jan 23, 2015
So earlier I misread this and said I agreed. But actually my view is that of @alexcrichton, namely that with specialization we'd prefer |
alexcrichton
commented
Jan 23, 2015
Hm, one downside of this that I forgot is that the quality of many error messages will go down significantly in some cases. I think that "avoiding panicking as much as possible" is certainly an important method of using the standard library, but it's by no means the only way of using it! An example is that |
wycats
commented
Jan 23, 2015
I think this is unavoidable given the tools we have available today, but I consider this to be the correct behavior in Ruby: Printing out escape sequences in |
carllerche
commented
Jan 23, 2015
I don't believe that the ruby exception throwing is the correct analogy to ifletErr(e) = risky_business(){panic!("attempted to perform risky business -- failed with {}; context={}; more-context={}",
e, foo, bar);}In which case, a good Imagine the case where the The argument that is being used here is that error types will implement Simply implement Defaulting to tl;dr Unwrapping a |
wycats
commented
Jan 23, 2015
@carllerche For tests, couldn't you implement an |
carllerche
commented
Jan 23, 2015
My usage of unwrap in tests is not for assertions, it's to destructure data such that if it is not in the expected structure, it will fail hard. In the cases where the unwrap fails, I simply want debug info so that I can figure out what went wrong. Again, one should not be using |
wycats
commented
Jan 23, 2015
@carllerche I am talking about some trait that you could implement that expresses precisely what you're trying to do: "I want to to destructure a Result such that if it is not in the expected structure, it will fail hard and use the Debug trait to display the Error variant" |
carllerche
commented
Jan 23, 2015
When outputting errors there really are 2 cases.
ifletErr(e) = risky_business(){panic!("attempted to perform risky business -- failed with {}; context={}; more-context={}",
e, foo, bar);}In this case, you definitely want the
|
wycats
commented
Jan 23, 2015
@carllerche I think the correct solution given specialization is probably something like an |
SimonSapin
commented
Jan 23, 2015
To add to this, I’m also very concerned about I don’t care about the error message when using If a nicer error message is desired, |
alexcrichton
commented
Jan 23, 2015
@SimonSapin arguably a type like |
tbu-
commented
Jan 23, 2015
@alexcrichton IIRC the documentation suggests to always use |
This is currently required by `Result<T, ImageError>::unwrap`. Fixes build errors in test files.
SimonSapin
commented
Jan 23, 2015
As @tbu- said, it’s so that I can use I’ve experimented to extend the |
SimonSapin
commented
Jan 23, 2015
I’ve also used |
aturon
commented
Jan 23, 2015
As far as I can tell, there's widespread agreement that we should revert to bounding by @alexcrichton would you like to make that change, or shall I? |
alexcrichton
commented
Jan 23, 2015
@tbu- yes and I believe the general practice is to use custom error types instead of @SimonSapin Yes currently I've created a PR for this change: rust-lang/rust#21558 |
SimonSapin
commented
Jan 23, 2015
By the way, (though this might be getting off-topic, sorry) I don’t really see the point of the |
aturon
commented
Jan 23, 2015
The intent was to standardize on functionality that production error types would implement; it also provides the cause chaining hooks that @mitsuhiko's ideas work from. FWIW, I would like to move forward with some of his ideas, but they basically are back-compat since they just use the |
A recent RFC split what was previously
fmt::Showinto two traits,fmt::Showandfmt::String, with format specifiers{:?}and{}respectively.That RFC did not, however, establish complete conventions for when to implement which of the traits, nor what is expected from the output. That's what this RFC seeks to do.
Rendered