user1244932
user1244932

Reputation: 8112

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:

  1. N 70.152351
  2. N 70° 09' 08"

There is also an additional flag; when it is false, I need something like:

  1. - --.------
  2. - --° -' -"

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?

Upvotes: 4

Views: 2162

Answers (3)

dpc.pw
dpc.pw

Reputation: 3466

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.

Upvotes: 4

Shepmaster
Shepmaster

Reputation: 431439

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.

Upvotes: 6

набиячлэвэли
набиячлэвэли

Reputation: 4677

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());
}

Upvotes: 2

Related Questions