From 255cbfe439771f9c532030662421471c558b497e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 7 Dec 2014 18:01:30 -0800 Subject: [PATCH 1/5] RFC: Split Show into String and Show Today's `Show` trait will be tasked with the purpose of providing the ability to inspect the representation of implementors of the trait. A new trait, `String`, will be introduced to the `std::fmt` module to in order to represent data that can essentially be serialized to a string, typically representing the precise internal state of the implementor. The `String` trait will take over the `{}` format specifier and the `Show` trait will move to the now-open `{:?}` specifier. --- text/0000-show-stabilization.md | 183 ++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 text/0000-show-stabilization.md diff --git a/text/0000-show-stabilization.md b/text/0000-show-stabilization.md new file mode 100644 index 00000000000..3caaa3f8d29 --- /dev/null +++ b/text/0000-show-stabilization.md @@ -0,0 +1,183 @@ +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Today's `Show` trait will be tasked with the purpose of providing the ability to +inspect the representation of implementors of the trait. A new trait, `String`, +will be introduced to the `std::fmt` module to in order to represent data that +can essentially be serialized to a string, typically representing the precise +internal state of the implementor. + +The `String` trait will take over the `{}` format specifier and the `Show` trait +will move to the now-open `{:?}` specifier. + +# Motivation + +The formatting traits today largely provide clear guidance to what they are +intended for. For example the `Binary` trait is intended for printing the binary +representation of a data type. The ubiquitous `Show` trait, however, is not +quite well defined in its purpose. It is currently used for a number of use +cases which are typically at odds with one another. + +One of the use cases of `Show` today is to provide a "debugging view" of a type. +This provides the easy ability to print *some* string representation of a type +to a stream in order to debug an application. The `Show` trait, however, is also +used for printing user-facing information. This flavor of usage is intended for +display to all users as opposed to just developers. Finally, the `Show` trait is +connected to the `ToString` trait providing the `to_string` method +unconditionally. + +From these use cases of `Show`, a number of pain points have arisen over time: + +1. It's not clear whether all types should implement `Show` or not. Types like + `Path` quite intentionally avoid exposing a string representation (due to + paths not being valid UTF-8 always) and hence do not want a `to_string` + method to be defined on them. +2. It is quite common to use `#[deriving(Show)]` to easily print a Rust + structure. This is not possible, however, when particular members do not + implement `Show` (for example a `Path`). +3. Some types, such as a `String`, desire the ability to "inspect" the + representation as well as printing the representation. An inspection mode, + for example, would escape characters like newlines. +4. Common pieces of functionality, such as `assert_eq!` are tied to the `Show` + trait which is not necessarily implemented for all types. + +The purpose of this RFC is to clearly define what the `Show` trait is intended +to be used for, as well as providing guidelines to implementors of what +implementations should do. + +# Detailed Design + +As described in the motivation section, the intended use cases for the current +`Show` trait are actually motivations for two separate formatting traits. One +trait will be intended for all Rust types to implement in order to easily allow +debugging values for macros such as `assert_eq!` or general `println!` +statements. A separate trait will be intended for Rust types which are "round +trippable" from a string. These types can be represented as a string in a +non-lossy fashion and are intended for general consumption by more than just +developers. + +This RFC proposes naming these two traits `Show` and `String`, respectively. + +## The `String` trait + +A new formatting trait will be added to `std::fmt` as follows: + +```rust +pub trait String for Sized? { + fn fmt(&self, f: &mut Formatter) -> Result; +} +``` + +This trait is identical to all other formatting traits except for its name. The +`String` trait will be used with the `{}` format specifier, typically considered +the default specifier for Rust. + +An implementation of the `String` trait is an assertion that the type can be +faithfully represented as a UTF-8 string at all times. In many cases the type +can actually be precisely reconstructed from a string such that the following +relation is true: + +```rust +assert_eq!(foo, from_str(format!("{}", foo).as_slice()).unwrap()); +``` + +It is **not** expected that all types implement the `String` trait. Not all +types can satisfy the purpose of this trait, and for example the following types +will not implement the `String` trait: + +* `Path` will abstain as it is not guaranteed to contain valid UTF-8 data. +* `CString` will abstain for the same reasons as `Path`. +* `RefCell` will abstain as it may not be accessed at all times to be + represented as a `String`. +* `Weak` references will abstain for the same reasons as `RefCell`. + +Almost all types that implement `Show` in the standard library today, however, +will implement the `String` trait. For example all primitive integer types, +vectors, slices, strings, and containers will all implement the `String` trait. +The output format will not change from what it is today (no extra escaping or +debugging will occur). + +The compiler will **not** provide an implementation of `#[deriving(String)]` for +types. + +## The `Show` trait + +The current `Show` trait will not change location nor definition, but it will +instead move to the `{:?}` specifier instead of the `{}` specifier (which +`String` now uses). + +An implementation of the `Show` trait is expected for **all** types in Rust and +provides very few guarantees about the output. Output will typically represent +the internal state as faithfully as possible, but it is not expected that this +will always be true. + +The purpose of the `Show` trait is to facilitate debugging Rust code which +implies that it needs to be maximally useful by extending to all Rust types. All +types in the standard library which do not currently implement `Show` will gain +an implementation of the `Show` trait including `Path`, `RefCell`, and `Weak` +references. + +Many implementations of `Show` in the standard library will differ from what +they currently are today. For example `str`'s implementation will escape all +characters such as newlines and tabs in its output. Primitive integers will +print the suffix of the type after the literal in all cases. Characters will +also be printed with surrounding single quotes while escaping values such as +newlines. The purpose of these implementations are to provide debugging views +into these types. + +Implementations of the `Show` trait are expected to never `panic!` and always +produce valid UTF-8 data. The compiler will continue to provide a +`#[deriving(Show)]` implementation to facilitate printing and debugging +user-defined structures. + +## The `ToString` trait + +Today the `ToString` trait is connected to the `Show` trait, but this RFC +proposes wiring it to the newly-proposed `String` trait instead. This switch +enables users of `to_string` to rely on the same guarantees provided by `String` +as well as not erroneously providing the `to_string` method on types that are +not intended to have one. + +It is strongly discouraged to provide an implementation of the `ToString` trait +and not the `String` trait. + +# Drawbacks + +It is inherently easier to understand fewer concepts from the standard library +and introducing multiple traits for common formatting implementations may lead +to frequently mis-remembering which to implement. It is expected, however, that +this will become such a common idiom in Rust that it will become second nature. + +This RFC establishes a convention that `Show` and `String` produce valid UTF-8 +data, but no static guarantee of this requirement is provided. Statically +guaranteeing this invariant would likely involve adding some form of +`TextWriter` which we are currently not willing to stabilize for the 1.0 +release. + +The default format specifier, `{}`, will quickly become unable to print many +types in Rust. Without a `#[deriving]` implementation, manual implementations +are predicted to be fairly sparse. This means that the defacto default may +become `{:?}` for inspecting Rust types, providing pressure to re-shuffle the +specifiers. Currently it is seen as untenable, however, for the default output +format of a `String` to include escaped characters (as opposed to printing the +string). Due to the debugging nature of `Show`, it is seen as a non-starter to +make it the "default" via `{}`. + +# Alternatives + +The names `String` and `Show` may not necessarily imply "user readable" and +"debuggable". An alternative proposal would be to use `Show` for user +readability and `Inspect` for debugging. This alternative also opens up the door +for other names of the debugging trait like `Repr`. This RFC, however, has +chosen `String` for user readability to provide a clearer connection with the +`ToString` trait as well as emphasizing that the type can be faithfully +represented as a `String`. Additionally, this RFC considers the name `Show` +roughly on par with other alternatives and would help reduce churn for code +migrating today. + +# Unresolved Questions + +None at this time. From 4782557dc4280601983239899216f346c4664ff8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 7 Dec 2014 18:09:21 -0800 Subject: [PATCH 2/5] Add a drawback of the String trait as defined --- text/0000-show-stabilization.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/0000-show-stabilization.md b/text/0000-show-stabilization.md index 3caaa3f8d29..ebf50e2c96a 100644 --- a/text/0000-show-stabilization.md +++ b/text/0000-show-stabilization.md @@ -166,6 +166,9 @@ format of a `String` to include escaped characters (as opposed to printing the string). Due to the debugging nature of `Show`, it is seen as a non-starter to make it the "default" via `{}`. +It may be too ambitious to define that `String` is a non-lossy representation of +a type, eventually motivating other formatting traits. + # Alternatives The names `String` and `Show` may not necessarily imply "user readable" and From 48f778e2adc0b933319ce8c02bff64c34c5ed252 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 7 Dec 2014 19:26:21 -0800 Subject: [PATCH 3/5] Clarify FromStr and String --- text/0000-show-stabilization.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/text/0000-show-stabilization.md b/text/0000-show-stabilization.md index ebf50e2c96a..d0a0a5ef28a 100644 --- a/text/0000-show-stabilization.md +++ b/text/0000-show-stabilization.md @@ -76,14 +76,17 @@ This trait is identical to all other formatting traits except for its name. The the default specifier for Rust. An implementation of the `String` trait is an assertion that the type can be -faithfully represented as a UTF-8 string at all times. In many cases the type -can actually be precisely reconstructed from a string such that the following -relation is true: +faithfully represented as a UTF-8 string at all times. If the type can be +reconstructed from a string, then the following relation must be true: ```rust assert_eq!(foo, from_str(format!("{}", foo).as_slice()).unwrap()); ``` +If the type cannot necessarily be reconstructed from a string, then the output +may be less descriptive than the type can provide, but it is guaranteed to be +human readable for all users. + It is **not** expected that all types implement the `String` trait. Not all types can satisfy the purpose of this trait, and for example the following types will not implement the `String` trait: From c3809637b0fe7db1d1a66bc0161bc0dde476222f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 7 Dec 2014 20:30:42 -0800 Subject: [PATCH 4/5] Add a sentence about not using Show output for reconstruction --- text/0000-show-stabilization.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-show-stabilization.md b/text/0000-show-stabilization.md index d0a0a5ef28a..abb8dd5fe09 100644 --- a/text/0000-show-stabilization.md +++ b/text/0000-show-stabilization.md @@ -115,7 +115,8 @@ instead move to the `{:?}` specifier instead of the `{}` specifier (which An implementation of the `Show` trait is expected for **all** types in Rust and provides very few guarantees about the output. Output will typically represent the internal state as faithfully as possible, but it is not expected that this -will always be true. +will always be true. The output of `Show` should never be used to reconstruct +the object itself as it is not guaranteed to be possible to do so. The purpose of the `Show` trait is to facilitate debugging Rust code which implies that it needs to be maximally useful by extending to all Rust types. All From fb4f25cab40b239fb2519c98857d35542f3f60e2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 18 Dec 2014 23:56:20 -0800 Subject: [PATCH 5/5] Tweak wording to recommend, but not require round-tripping --- text/0000-show-stabilization.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/text/0000-show-stabilization.md b/text/0000-show-stabilization.md index abb8dd5fe09..394d12c08cd 100644 --- a/text/0000-show-stabilization.md +++ b/text/0000-show-stabilization.md @@ -54,10 +54,10 @@ As described in the motivation section, the intended use cases for the current `Show` trait are actually motivations for two separate formatting traits. One trait will be intended for all Rust types to implement in order to easily allow debugging values for macros such as `assert_eq!` or general `println!` -statements. A separate trait will be intended for Rust types which are "round -trippable" from a string. These types can be represented as a string in a -non-lossy fashion and are intended for general consumption by more than just -developers. +statements. A separate trait will be intended for Rust types which are +faithfully represented as a string. These types can be represented as a string +in a non-lossy fashion and are intended for general consumption by more than +just developers. This RFC proposes naming these two traits `Show` and `String`, respectively. @@ -77,7 +77,8 @@ the default specifier for Rust. An implementation of the `String` trait is an assertion that the type can be faithfully represented as a UTF-8 string at all times. If the type can be -reconstructed from a string, then the following relation must be true: +reconstructed from a string, then it is recommended, but not required, that the +following relation be true: ```rust assert_eq!(foo, from_str(format!("{}", foo).as_slice()).unwrap());