For a custom type T: Display + FromStr is it expected that u = T::from_str(&t.to_string()); results in u: Ok(t) for t: T?
I ask, because I have a use case, where a type implements Display to provide a human-readable representation, which is not the same that FromStr expects.
Is this considered a bad practice, or is it not even expected that Display generates a representation that can be parsed by FromStr for any type?
I wouldn't say it's normal for them to disagree. Having impl FromStr for MyType implies the type has predefined textual representation. impl Display for MyType should print textual representation of the type, why do you want to make them disagree?
In my current case, I have a bitmask of weekdays (related post). The API transfers those as a string of "0"s and "1"s, e.g. "1101000", which is not so well readable for humans. Hence, the implementation of Display formats the mask differently, i.e. with the string representation of HashMap<chrono::Weekday>, e.g. "{Mon, Tue, Thu}", which is arguably more readable.
Why do you want to show more readable representation? Is it part of your API format? Or just for logging? If it's for the latter, you can impl Debug for pretty format instead.
I would suggest that FromStr and Display should be defined if there is a canonical string representation of your data, and that they should compose into an identity function (modulo error handling).
However a nice trick, that I've seen in the wider Rust ecosystem, is to create new-type wrappers that provide other display formats. For example:
use std::str::FromStr;
use std::fmt;
struct Foo;
impl Foo {
/// Returns struct that provides a human-readable implementation of Display
fn to_human_readable_display(&self) -> DisplayFoo<'_> {
DisplayFoo(&self)
}
}
impl FromStr for Foo {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
todo!()
}
}
impl fmt::Display for Foo {
// Compatible with FromStr implementation
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
todo!()
}
}
struct DisplayFoo<'a>(&'a Foo);
impl fmt::Display for DisplayFoo<'_> {
// Human-readable implementation
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
todo!()
}
}
This is especially nice if you want to have multiple ways to display your data (for example you can have many different ways to format date-time).
Using a view type is something, that I've already done in past projects.
However, in my current situation I think it's overkill.
I'll use the debug implementation for now.
That links to someone asserting that it is a guidline, but it really is not. If it really was a guidline it would be mentioned in the documentation of either std::fmt::Display or std::str::FromStr, but it is not.
Display and FromStr are about going to / coming from the "one obvious string representation," and that implies that they use the same string representation.
No, many guidelines are not mentioned in the reference of std.
Of course you can, technically, go ahead and ignore this advice, but be prepared that people will be mad (and rightfully so) if your Display impl doesn't parse back using FromStr.
Somewhat recently I got a section added to the Display trait documentation on this. It still hedges a bit since we didn't want to say any existing usage was wrong per say, but we do now directly suggest that Display should use an “obviously canonical” format when one exists.
Because a type can only have one Display implementation, it is often preferable to only implement Display when there is a single most “obvious” way that values can be formatted as text. This could mean formatting according to the “invariant” culture and “undefined” locale, or it could mean that the type display is designed for a specific culture/locale, such as developer logs.
I've intended for a while to add the other direction to the FromStr documentation, that if a type does impl both Display and FromStr, there's a general expectation for val.to_string().parse() to succeed. It might not always be a true identity function, but that the round-trip works follows from both recognizing the same “obviously canonical” textual format.
I just haven't gotten around to writing and PRing the wording yet, as it's lower priority than other things I'm also working on. But I do believe that this sentiment is held and that docs pointing this out more clearly would be accepted if hedged similarly to the above.
And the docs now mention this pattern as well. Continuing the quote:
If not all values have a justifiably canonical textual format or if you want to support alternative formats not covered by the standard set of possible formatting traits, the most flexible approach is display adapters: methods like str::escape_default or Path::display which create a wrapper implementing Display to output the specific display format.