iliekturtles / uom

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

Extending uom with a custom Trait/Method #243

Closed Nodraak closed 3 years ago

Nodraak commented 3 years ago

Hi,

First of all, thanks a lot for this wonderful lib! (I like "No more crashing your climate orbiter!" haha)

I'm not a fan of x.powi(uom::typenum::P2::new());, and I'm trying to have either (by order of preference) x.squared() or squared(x) or x.powi(2).

I've tried several things, including reading uom's code and asking Google, but I've not been able to have something that compiles successfully. I'm not a Rust expert and I'm probably missing the vocabulary to find what I'm looking for.

The closest (?) I got was:

use uom;
use uom::si::{Dimension, Units, Quantity};

// I define my trait
pub trait MathExt {
    fn squared(&self) -> Self;
}

// I "overload" uom with my trait
// (Inspired from src/system.rs:568 and src/system.rs:818)
impl<D, U, V> MathExt for Quantity<D, U, V>
where
    D: Dimension + ?Sized,
    U: Units<V> + ?Sized,
    V: uom::num::Num + uom::Conversion<V> + uom::num_traits::float::Float,
{
    fn squared(&self) -> Self {
        self.new(self.value.powi(2))
    }
}

// Error messages I got include
// * The method exists but the following trait bounds were not satisfied
// * The trait cannot be made into an object

I'm probably not missing much, could you help me find and fix what is wrong?

Thanks in advance

adamreichold commented 3 years ago

I think the problem is that your signature

fn squared(&self) -> Self {

cannot work as the squared quantity has a different dimension than the quantity itself, e.g. area/square meters after squaring length/meters.

The actual signature for powi is quite complicated as it needs to multiply all the base dimensions:

#[inline(always)]
pub fn powi<E>(
    self, _e: E
) -> Quantity<$quantities<$($crate::typenum::Prod<D::$symbol, E>),+>, U, V>
where
    $(D::$symbol: $crate::lib::ops::Mul<E>,
    <D::$symbol as $crate::lib::ops::Mul<E>>::Output: $crate::typenum::Integer,)+
    D::Kind: $crate::marker::Mul,
    E: $crate::typenum::Integer,
    V: $crate::num::Float,
{
    Quantity {
        dimension: $crate::lib::marker::PhantomData,
        units: $crate::lib::marker::PhantomData,
        value: self.value.powi(E::to_i32()),
    }
}

and I fear that implementing squared outside of the system! macro might be impossible.

(As a workaround, you could have a macro_rules! squared which calls $x.powi(uom::typenum::P2::new()); if the visual clutter is the main issue.)

Nodraak commented 3 years ago

the squared quantity has a different dimension as the quantity itself

Ahhh of course, you're right! Indeed, it's more complicated that it sounds...

As a workaround, you could have a macro_rules! squared

Yeah, I think that's the best option, thank you for the suggestion!