简体   繁体   中英

Generic numeric function using traits and `match`

I'm trying to build a generic formatting function that takes two integers T as nominator and denominator, and formats the fraction according to some simple rules.

I had a look at the num traits, however, still can't make it work. I'm already with much more verbose code than I wanted to so I thought it's time to ask for help.

extern crate num;
use num::{Integer,FromPrimitive};

fn format_rational<T>(n: T, d: T) -> String
    where T: Integer + FromPrimitive + fmt::Display
{
    let one: T = FromPrimitive::from_u16(1).unwrap();
    let ten: T = FromPrimitive::from_u16(10).unwrap();
    let hun: T = FromPrimitive::from_u16(100).unwrap();
    let tho: T = FromPrimitive::from_u16(1000).unwrap();

    let precision = match d {
        one => Some(1),
        hun => Some(2),
        tho => Some(3),
        _ => None,
    };

    match precision {
        Some(p) => format!("{:.*}", p, n as f32 / d as f32),
        None => format!("{} / {}", n, d),
    }
}

This errors with

non-primitive cast: `T` as `f32`

But when I change the first format! line to

Some(p) => format!("{:.*}", p, f32::from(n) / f32::from(d))

I get this error:

the trait bound `f32: std::convert::From<T>` is not satisfied

which I interpret as "From does not exist for type f32", which makes sense. How is f32 possible here when the trait is Num , which implies Eq , which implies it should exclude floats? (as floats only implement PartialEq ). Finally, is there a way to simply work with From<T> ?

What am I missing? Or can the problem be solved in another (easier) way? Thanks!

I solved it today. Are there better ways of doing this?

extern crate num;
use std::fmt;
use num::{ToPrimitive};

pub fn format_rational<T>(n: T, d: T) -> String
    where T: ToPrimitive + fmt::Display
{
    let precision = match d.to_usize().unwrap() {
        1 | 10 => Some(1),
        100 => Some(2),
        1000 => Some(3),
        _ => None,
    };

    match precision {
        Some(p) => format!("{:.*}", p, ToPrimitive::to_f32(&n).unwrap() / ToPrimitive::to_f32(&d).unwrap()),
        None => format!("{} / {}", n, d),
    }
}

Avoiding some unwrap s, compared to your answer:

extern crate num;
use std::fmt;
use num::{ToPrimitive};

pub fn format_rational<T>(n: T, d: T) -> String
    where
        T: ToPrimitive + std::ops::Div + fmt::Display,
        <T as std::ops::Div>::Output: fmt::Display,
{
    let precision = match d.to_usize() {
        Some(1) | Some(10) => Some(1),
        Some(100) => Some(2),
        Some(1000) => Some(3),
        _ => None,
    };

    match precision {
        Some(p) => format!("{:.*}", p, n.div(d)),
        None => format!("{} / {}", n, d),
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM