fmt::Debug a number field with differing base (rust) - rust

I want to the Debug format of a struct number field to show a decimal value and a hexadecimal value.
So given rust struct (rust playground)
#[derive(Default)]
pub struct S1 {
pub num1: u64,
}
fn main() {
let mut s1 = S1::default();
s1.num1 = 0xFF;
println!("{:?}", s1)
}
I would like fmt::Debug implementation (i.e. format string "{:?}") to show two different based values for field num1, i.e. to print
S1 { num1: 255 (0xFF) }
(I'm not sure how to solve the easier problem: format the numeric field as hexadecimal).
Here is what I have for fmt::Debug implementation
impl std::fmt::Debug for S1 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("S1")
.field("num1", &self.num1)
.finish()
}
}
I do not know which part of std::fmt module would help me.

You can use format_args! to construct the formatting arguments in a low-cost way:
impl std::fmt::Debug for S1 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("S1")
.field("num1", &format_args!("{0:?} (0x{0:X})", &self.num1))
.finish()
}
}

Related

Is there a nicer way to implement Display for structs that own collections of things with DIsplay?

I keep finding myself writing Display for structs that hold Vec of some type that implements Display. For example:
use std::fmt::Display;
struct VarTerm {
pub coeffecient: usize,
pub var_name: String,
}
impl Display for VarTerm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{}", self.coeffecient, self.var_name)
}
}
struct Function {
pub terms: Vec<VarTerm>,
}
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let strings = self
.terms
.iter()
.map(|s| format!("{}", s))
.collect::<Vec<String>>()
.join(" + ");
write!(f, "{}", strings)
}
}
fn main() {
let my_function = Function {
terms: vec![
VarTerm {coeffecient: 2,var_name: "x".to_string(),},
VarTerm {coeffecient: 4,var_name: "y".to_string(),},
VarTerm {coeffecient: 5,var_name: "z".to_string(),},
],
};
println!("All that work to print something: {}", my_function)
}
This looks bulky and ugly to me in a bunch of places - coming from higher-level languages I'm never a fan of the .iter()/.collect() sandwiching (I kind of get why it's needed but it's annoying when 90+ percent of the time I'm just going from Vec to Vec). In this case it's also compounded by the format!() call, which I swear has to be the wrong way to do that.
I'm not sure how much of this is inherent to Rust and how much is me not knowing the right way. I want to get as close as possible to something like:
self.terms.map(toString).join(" + "), which is about what I'd expect in something like Scala.
How close can I get to there? Along the way, is there anything to be done about the aforementioned iter/collect sandwiching in general?
In an eerie coincidence, literally 2 minutes ago I looked at a few methods in the itertools crate. How about this one:
https://docs.rs/itertools/latest/itertools/trait.Itertools.html#method.join
fn join(&mut self, sep: &str) -> String
where
Self::Item: Display
Combine all iterator elements into one String, separated by sep.
Use the Display implementation of each element.
use itertools::Itertools;
assert_eq!(["a", "b", "c"].iter().join(", "), "a, b, c");
assert_eq!([1, 2, 3].iter().join(", "), "1, 2, 3");
EDIT: Additionally, whenever you ask yourself if there was a nicer way to implement a particular trait, especially when the implementation would be somewhat recursive, you should look if there's a derive macro for that trait. Turns out there is, albeit in a separate crate:
https://jeltef.github.io/derive_more/derive_more/display.html
Example:
#[derive(Display)]
#[display(fmt = "({}, {})", x, y)]
struct Point2D {
x: i32,
y: i32,
}
If you find yourself repeating this a lot you might want to move the .into_iter()/.collect() into a specific trait at the cost of generality and composability.
You can also pass ToString::to_string to map.
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let strings = self.terms.map(ToString::to_string).join(" + ");
f.write_str(&strings)
}
}
trait VecMap<'a, T: 'a, U>: IntoIterator<Item = T> {
fn map(self, f: impl FnMut(T) -> U) -> Vec<U>;
}
impl<'a, T: 'a, U> VecMap<'a, &'a T, U> for &'a Vec<T> {
fn map(self, f: impl FnMut(&'a T) -> U) -> Vec<U> {
self.into_iter().map(f).collect()
}
}

Formatting a struct which implements fmt::Display doesn't respect format specifiers

I have this example code.
use std::fmt;
struct MyStruct {
mystring: String
}
impl fmt::Display for MyStruct {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "^.^ My String is *{}*. ^.^", self.mystring)
}
}
fn main() {
let mystruct = MyStruct { mystring: "GOAT".to_string() };
println!("|{:^80}|", mystruct);
println!("|{:^80}|", "^.^ My String is *GOAT*. ^.^")
}
(Playground.)
I expect whatever is the output of the MyStruct::fmt() implementation, it will be written to a 80 character wide field, aligned to center, due to the {:^80} specifier. This is not what happens. The output doesn't respect the specifier, it's the same as if I simply wrote {}.
This is the output of my example:
|^.^ My String is *GOAT*. ^.^|
| ^.^ My String is *GOAT*. ^.^ |
I'd expect these two lines to look identical.
How can I implement my formatter so that it respects the format specifier it is formatted with?
The pad() function respects formatting flags, however it expects a &str so you need to call format!():
impl fmt::Display for MyStruct {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(&format!("^.^ My String is *{}*. ^.^", self.mystring))
}
}
Playground.

Is it possible to pass formatting expression?

For example, I have a very simple struct which contains only a float, but I want to implement the Display trait for this struct while also be able to set precision for the float inside of the struct...
Sorry I'm very bad at describing things, here's the sample code:
struct NewFloat(f64);
impl std::fmt::Display for NewFloat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(&self.0.to_string())
}
}
fn main() {
let test: NewFloat = NewFloat(0.046);
println!("{:.2}", test); // 0.
println!("{:.2}", 0.046); // 0.05
}
I'd prefer printing test giving me same result as printing 0.046, which applies precision to floating points only and also rounds the number, how do I do that?
You can simply delegate to the existing implementation for field:
impl std::fmt::Display for NewFloat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
// or, for more explicitness:
// std::fmt::Display::fmt(&self.0, f)
}
}
Playground

How to implement Display on a trait object where the types already implement Display

I have some code which returns a trait object of type MyTrait so that it can return one of several different structs. I would like to implement the Display trait for the trait object so that I can print the object, with the details delegated to the various structs as they each need their own custom formatters.
I can achieve this by including a formatting method as part of the MyTrait definition, and then implementing Display for MyTrait and delegating - like this:
trait MyTrait {
fn is_even(&self) -> bool;
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
impl fmt::Display for MyTrait {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.my_fmt(f)
}
}
However, I already have the Display trait implemented for each of the structs which implement MyTrait. This means I end up with two methods for each struct which do the same thing - the fmt() method to satisfy the Display trait directly on the struct, and the my_fmt() method which is called by the code above. This seems clumsy and repetitive. Is there a simpler way to do it?
Here's a complete example program which illustrates the point. It's a little longer than I would have liked (it's based on the answer to my previous question Calling functions which return different types with shared trait and pass to other functions), but I couldn't think of a simpler way to illustrate the point. Of course, in this toy example the structs and the fmt functions are very simple; in my real application they are more complex.
use std::fmt;
trait MyTrait {
fn is_even(&self) -> bool;
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
struct First {
v: u8,
}
struct Second {
v: Vec<u8>,
}
impl MyTrait for First {
fn is_even(&self) -> bool {
self.v % 2 == 0
}
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
}
}
impl MyTrait for Second {
fn is_even(&self) -> bool {
self.v[0] % 2 == 0
}
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v[0])
}
}
fn make1() -> First {
First { v: 5 }
}
fn make2() -> Second {
Second { v: vec![2, 3, 5] }
}
// Implement Display for the structs and for MyTrait
impl fmt::Display for First {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
}
}
impl fmt::Display for Second {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v[0])
}
}
impl fmt::Display for MyTrait {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.my_fmt(f)
}
}
fn requires_mytrait<T: MyTrait + ?Sized>(v: &&T) {
println!("{:?}", v.is_even());
}
fn main() {
for i in 0..2 {
let v1;
let v2;
let v = match i {
0 => {
v1 = make1();
println!("> {}", v1); // Demonstrate that Display
// is implemented directly
// on the type.
&v1 as &MyTrait
}
_ => {
v2 = make2();
println!("> {}", v2); // Demonstrate that Display
// is implemented directly
// on the type.
&v2 as &MyTrait
}
};
requires_mytrait(&v);
println!("{}", v); // Here I print the trait object
}
}
Can anyone suggest a simpler, cleaner way to do this?
You can make Display a supertrait of MyTrait.
trait MyTrait: fmt::Display {
fn is_even(&self) -> bool;
}
This will make trait objects of MyTrait be Display. This only works if you expect all implementors of MyTrait to implement Display, but that was also the case in your previous solution.

What is an idiomatic Rust way to format a value to multiple kinds of strings?

I have latitude in degrees in the form of a f64 and I need to convert it into a String. At first I thought about implementing Display, like this:
struct Latitude(f64);
impl fmt::Display for Latitude {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", if self.0 > 0. { "N" } else { "S" }, self.0)
}
}
fn main() {
let lat: f64 = 45.;
println!("{}", Latitude(lat));
}
After that, I have additional requirements. I need convert to one of two representations:
N 70.152351
N 70° 09' 08"
There is also an additional flag; when it is false, I need something like:
- --.------
- --° -' -"
The most simple way to implement this will be:
fn format_lat(degree: f64, use_min_sec_variant: bool, is_valid: bool) -> String;
However, I don't see any free functions in the Rust standard library.
Maybe I should use struct Latitude(f64) and implement a to_string method? Or maybe I should implement some other trait?
You can create wrapper types with different types of formatting and return them as a method:
struct Latitude(f64);
struct MinutesSeconds(f64);
impl Latitude {
fn minutes_seconds(&self) -> MinutesSeconds {
MinutesSeconds(self.0)
}
}
impl fmt::Display for MinutesSeconds {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "latitude in a different format")
}
}
This is the same idea as Path::display.
As for the valid / invalid concept, it sounds like you really want a struct Latitude(Option<f64>) and then provide None when it is invalid.
Basically you're free to do what you want. Without more context on your goals, any method seems good enough for me.
Personally I'd do it as:
struct Latitude(f64);
impl Latitutde {
pub fn format_as_x(&self, f: &mut fmt::Formatter) -> fmt::Result<(), Error> {}
pub fn format_as_y(&self, f: &mut fmt::Formatter) -> fmt::Result<(), Error> {}
}
plus a Display trait + to_format_x() -> String convenience functions.
format_as_y are IMO best method since they handle errors, can take any formatter and don't need to allocate a String to return.
By the way, taking bool arguments is usually an anti-pattern: boolean trap.
You can impl Latitude if you need to pass arguments or impl ToString for Latitude if you don't:
use std::string::ToString;
struct Latitude(f64);
// If you need parameterisation on your conversion function
impl Latitude {
// You'd probably use a better-suited name
pub fn to_string_parameterised(&self, use_min_sec_variant: bool) -> String {
// Conversion code
format!("{} {}", self.0, use_min_sec_variant)
}
}
// If you don't need parameterisation and want to play nicely with 100% of all code elsewhere
impl ToString for Latitude {
fn to_string(&self) -> String {
// Conversion code
format!("{}", self.0)
}
}
fn main() {
let lat = Latitude(45.0);
println!("{}", lat.to_string_parameterised(false));
println!("{}", lat.to_string());
}

Resources