How can I access the specified precision when implementing Debug? - rust

I have a simple structure for holding intervals of f64s:
pub struct Interval {
pub min: f64,
pub max: f64
}
This code prints with hardcoded 3 decimal places:
impl fmt::Debug for Interval {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{:.3?} {:.3?}]", self.min, self.max)
}
}
I want to support println!("{:.6}", my_interval) to be able to print with desired precision.

As mentioned in the comments, use Formatter::precision. There is already an example of this in the documentation:
impl fmt::Binary for Vector2D {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let magnitude = (self.x * self.x + self.y * self.y) as f64;
let magnitude = magnitude.sqrt();
// Respect the formatting flags by using the helper method
// `pad_integral` on the Formatter object. See the method
// documentation for details, and the function `pad` can be used
// to pad strings.
let decimals = f.precision().unwrap_or(3);
let string = format!("{:.*}", decimals, magnitude);
f.pad_integral(true, "", &string)
}
}
For your case:
impl fmt::Debug for Interval {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let precision = f.precision().unwrap_or(3);
write!(f, "[{:.*?} {:.*?}]", precision, self.min, precision, self.max)
}
}

Related

Apply format arguments to struct members in Display implementation

I want to apply format arguments (for example to limit the amount of digits) to the members of my struct. My current implementation looks roughly like this:
use std::fmt;
#[derive(Debug)]
pub struct Vec2 { x: f32, y: f32 }
impl fmt::Display for Vec2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}, {}]", self.x, self.y)
}
}
If I use the automatically derived debug printing using e.g. {:.2?}, the arguments are applied and only 2 places after the decimal are printed. This doesn't seem to work with my Display implementation though using {:.2}. I tried to look at the Debug implementation using cargo expand but there are many functions specifically for Debug.
The easiest way will be to forward the formatting:
impl fmt::Display for Vec2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("[")?;
fmt::Display::fmt(&self.x, f)?;
f.write_str(", ")?;
fmt::Display::fmt(&self.y, f)?;
f.write_str("]")
}
}
Note that if you want to have all formatting specifiers, you have to implement additional traits. For floats, it's only std::fmt::LowerExp and std::fmt::UpperExp. You can make a little helper function to not duplicate code:
impl Vec2 {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
fmt_fn: fn(&f32, f: &mut fmt::Formatter<'_>) -> fmt::Result,
) -> fmt::Result {
f.write_str("[")?;
fmt_fn(&self.x, f)?;
f.write_str(", ")?;
fmt_fn(&self.y, f)?;
f.write_str("]")
}
}
impl fmt::Display for Vec2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt(f, fmt::Display::fmt)
}
}
impl fmt::LowerExp for Vec2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt(f, fmt::LowerExp::fmt)
}
}
impl fmt::UpperExp for Vec2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt(f, fmt::UpperExp::fmt)
}
}

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

Why is the width ignored for my custom formatter implementation?

When I have an enum with Display implemented and I try to print it formatted, the width I give it is ignored.
use std::fmt;
enum TestEnum {
A,
B,
}
impl fmt::Display for TestEnum {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TestEnum::A => write!(f, "A"),
TestEnum::B => write!(f, "B"),
}
}
}
fn main() {
println!("-{value:>width$}-", value = TestEnum::A, width = 3);
}
I expect it to output - A-, but it outputs -A-.
If I replace the value with the actual string instead of the enum, it does the right thing,
println!("-{value:>width$}-", value = "A", width = 3);
outputs
- A-
Why is the width being ignored? What do I need to do differently?
By using write! in your fmt implementation, you are overriding the format provided by its caller.
Instead you should call fmt on the strings themselves:
impl fmt::Display for TestEnum {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TestEnum::A => "A".fmt(f),
TestEnum::B => "B".fmt(f),
}
}
}
(Permalink to the playground)
This will correctly print - A-.

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