iliekturtles / uom

Units of measurement -- type-safe zero-cost dimensional analysis
Apache License 2.0
1.02k stars 97 forks source link

Question: Safe storage type casting. #172

Open jerry73204 opened 4 years ago

jerry73204 commented 4 years ago

Suppose we have a length stored in u32. How could I convert its storage type to f64? So far it can be done by this unsafe way:

let u32length = uom::si::u32::Length::new::<meter>(5);
let f64length = uom::si::f64::Length::new::<meter>(u32length.get::<meter>() as f64);

It extracts the stored value and thus the underlying SI unit is not checked among new and get. My question is that how can we cast the storage type with type safety?

iliekturtles commented 4 years ago

Currently there is no type-safe way to do this. I haven't thought about this in much depth, but adding TryFrom implementations using ToPrimitive is likely the way to go.

impl<Ul, Vl, Ur> ::lib::convert::From<Quantity<Ul, Vl>> for Quantity<Ur, XXX>
where
    Vl: ToPrimitive
{
    fn from(quantity: Quantity<Ul, Vl>) -> Self {
        Self { ..., change_base(quantity.value.to_XXX()) }
    }
}
adamreichold commented 4 years ago

Maybe it would be easier to just support "casting" from Quantity<D, SI<Vl>, Vl> and Quantity<D, SI<Vr>, Vr> for now, i.e. assuming the same base units. This should be safely possible provided the user can give us a function FnOnce(Vl) -> Vr, e.g. an interface like

fn cast(self, f: impl FnOce(Vl) -> Vr) -> Quantity<D, SI<Vr>, Vr>;

which would make it possible for the user to pick if casting using as f64 or traits like From/Into are more appropriate.

Personally, I have needed this limited conversion quite often (also between integer and floating point numbers where TryFrom is not supported yet), but have never need the conversion to another system of base units.