diff --git a/text/0000-show-string-guidelines.md b/text/0000-show-string-guidelines.md new file mode 100644 index 00000000000..15a20267170 --- /dev/null +++ b/text/0000-show-string-guidelines.md @@ -0,0 +1,166 @@ +- Start Date: 2015-01-08 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +A [recent RFC](https://github.com/rust-lang/rfcs/pull/504) split what was +previously `fmt::Show` into two traits, `fmt::Show` and `fmt::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. + +It turns out that, due to the suggested conventions and other +concerns, renaming the traits is also desirable. + +# Motivation + +Part of the reason for splitting up `Show` in the first place was some tension +around the various use cases it was trying to cover, and the fact that it could +not cover them all simultaneously. Now that the trait has been split, this RFC +aims to provide clearer guidelines about their use. + +# Detailed design + +The design of the conventions stems from two basic desires: + +1. It should be easy to generate a debugging representation of + essentially any type. + +2. It should be possible to create user-facing text output via convenient + interpolation. + +Part of the premise behind (2) is that user-facing output cannot automatically +be "composed" from smaller pieces of user-facing output (via, say, +`#[derive]`). Most of the time when you're preparing text for a user +consumption, the output needs to be quite tailored, and interpolation via +`format` is a good tool for that job. + +As part of the conventions being laid out here, the RFC proposes to: + +1. Rename `fmt::Show` to `fmt::Debug`, and +2. Rename `fmt::String` to `fmt::Display`. + +## Debugging: `fmt::Debug` + +The `fmt::Debug` trait is intended for debugging. It should: + +* Be implemented on every type, usually via `#[derive(Debug)]`. +* Never panic. +* Escape away control characters. +* Introduce quotes and other delimiters as necessary to give a clear + representation of the data involved. +* Focus on the *runtime* aspects of a type; repeating information such as + suffixes for integer literals is not generally useful since that data is + readily available from the type definition. + +In terms of the output produced, the goal is make it easy to make sense of +compound data of various kinds without overwhelming debugging output +with every last bit of type information -- most of which is readily +available from the source. The following rules give rough guidance: + +* Scalars print as unsuffixed literals. +* Strings print as normal quoted notation, with escapes. +* Smart pointers print as whatever they point to (without further annotation). +* Fully public structs print as you'd normally construct them: + `MyStruct { f1: ..., f2: ... }` +* Enums print as you'd construct their variants (possibly with special + cases for things like `Option` and single-variant enums?). +* Containers print using *some* notation that makes their type and + contents clear. (Since we lack literals for all container types, + this will be ad hoc). + +It is *not* a *requirement* for the debugging output to be valid Rust +source. This is in general not possible in the presence of private +fields and other abstractions. However, when it is feasible to do so, +debugging output *should* match Rust syntax; doing so makes it easier +to copy debug output into unit tests, for example. + +## User-facing: `fmt::Display` + +The `fmt::Display` trait is intended for user-facing output. It should: + +* Be implemented for scalars, strings, and other basic types. +* Be implemented for generic wrappers like `Option` or smart pointers, where + the output can be wholly delegated to a *single* `fmt::Display` implementation + on the underlying type. +* *Not* be implemented for generic containers like `Vec` or even `Result`, + where there is no useful, general way to tailor the output for user consumption. +* Be implemented for *specific* user-defined types as useful for an application, + with application-defined user-facing output. In particular, applications will + often make their types implement `fmt::Display` specifically for use in + `format` interpolation. +* Never panic. +* Avoid quotes, escapes, and so on unless specifically desired for a user-facing purpose. +* Require use of an explicit adapter (like the `display` method in + `Path`) when it potentially looses significant information. + +A common pattern for `fmt::Display` is to provide simple "adapters", which are +types wrapping another type for the sole purpose of formatting in a certain +style or context. For example: + +```rust +pub struct ForHtml<'a, T>(&'a T); +pub struct ForCli<'a, T>(&'a T); + +impl MyInterestingType { + fn for_html(&self) -> ForHtml { ForHtml(self) } + fn for_cli(&self) -> ForCli { ForCli(self) } +} + +impl<'a> fmt::Display for ForHtml<'a, MyInterestingType> { ... } +impl<'a> fmt::Display for ForCli<'a, MyInterestingType> { ... } +``` + +## Rationale for format specifiers + +Given the above conventions, it should be clear that `fmt::Debug` is +much more commonly *implemented* on types than `fmt::Display`. Why, +then, use `{}` for `fmt::Display` and `{:?}` for `fmt::Debug`? Aren't +those the wrong defaults? + +There are two main reasons for this choice: + +* Debugging output usually makes very little use of interpolation. In general, + one is typically using `#[derive(Show)]` or `format!("{:?}", + something_to_debug)`, and the latter is better done via + [more direct convenience](https://github.com/SimonSapin/rust-std-candidates#the-show-debugging-macro). + +* When creating tailored string output via interpolation, the expected "default" + formatting for things like strings is unquoted and unescapted. It would be + surprising if the default specifiers below did not yield `"hello, world!" as the + output string. + + ```rust + format!("{}, {}!", "hello", "world") + ``` + +In other words, although more types implement `fmt::Debug`, most +meaningful uses of interpolation (other than in such implementations) +will use `fmt::Display`, making `{}` the right choice. + +## Use in errors + +Right now, the (unstable) `Error` trait comes equipped with a `description` +method yielding an `Option`. This RFC proposes to drop this method an +instead inherit from `fmt::Display`. It likewise proposes to make `unwrap` in +`Result` depend and use `fmt::Display` rather than `fmt::Debug`. + +The reason in both cases is the same: although errors are often thought of in +terms of debugging, the messages they result in are often presented directly to +the user and should thus be tailored. Tying them to `fmt::Display` makes it +easier to remember and add such tailoring, and less likely to spew a lot of +unwanted internal representation. + +# Alternatives + +We've already explored an alternative where `Show` tries to play both of the +roles above, and found it to be problematic. There may, however, be alternative +conventions for a multi-trait world. The RFC author hopes this will emerge from +the discussion thread. + +# Unresolved questions + +(Previous questions here have been resolved in an RFC update).