iliekturtles / uom

Units of measurement -- type-safe zero-cost dimensional analysis

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Subtracting thermodynamic temperatures should produce a temperature interval

ryanolf-tb opened this issue · comments

I have an application where I need to take a difference of thermodynamic temperatures, and my expectation would be that this produces a temperature interval. At present, this operation is not allowed.

An additional Sub impl should be added and it looked trivial so I tried and get the following error. TLDR: without specialization the default Sub impl conflicts with the desired impl and there isn't a way to add a negative bound to the new impl that is the opposite of D::Kind: $crate::marker::$AddSubTrait,.

I'll keep thinking on ways to work around the issue without specialization.

   Compiling uom v0.33.0 (/home/mjboutin/uom)
error[E0119]: conflicting implementations of trait `std::ops::Sub<si::Quantity<(dyn si::Dimension<M = typenum::Z0, Kind = (dyn si::marker::TemperatureKind + 'static), N = typenum::Z0, L = typenum::Z0, I = typenum::Z0, T = typenum::Z0, J = typenum::Z0, Th = typenum::PInt<typenum::UInt<typenum::UTerm, typenum::B1>>> + 'static), _, _>>` for type `si::Quantity<(dyn si::Dimension<M = typenum::Z0, Kind = (dyn si::marker::TemperatureKind + 'static), N = typenum::Z0, L = typenum::Z0, I = typenum::Z0, T = typenum::Z0, J = typenum::Z0, Th = typenum::PInt<typenum::UInt<typenum::UTerm, typenum::B1>>> + 'static), _, _>`
   --> src/system.rs:395:17
    |
395 | /                 impl<D, Ul, Ur, V> $crate::lib::ops::$AddSubTrait<Quantity<D, Ur, V>>
396 | |                     for Quantity<D, Ul, V>
397 | |                 where
398 | |                     D: Dimension + ?Sized,
...   |
413 | |                     }
414 | |                 }}
    | |_________________^ conflicting implementation for `si::Quantity<(dyn si::Dimension<M = typenum::Z0, Kind = (dyn si::marker::TemperatureKind + 'static), N = typenum::Z0, L = typenum::Z0, I = typenum::Z0, T = typenum::Z0, J = typenum::Z0, Th = typenum::PInt<typenum::UInt<typenum::UTerm, typenum::B1>>> + 'static), _, _>`
    |
   ::: src/si/thermodynamic_temperature.rs:177:1
    |
177 | / impl<Ul, Ur, V> crate::lib::ops::Sub<ThermodynamicTemperature<Ur, V>>
178 | |     for ThermodynamicTemperature<Ul, V>
179 | | where
180 | |     Ul: super::Units<V> + ?Sized,
...   |
193 | |     }
194 | | }
    | |_- first implementation here
    |
   ::: src/si/mod.rs:10:1
    |
10  | / system! {
11  | |     /// [International System of Quantities](https://jcgm.bipm.org/vim/en/1.6.html) (ISQ).
12  | |     ///
13  | |     /// ## Generic Parameters
...   |
112 | |     }
113 | | }
    | |_- in this macro invocation
    |
    = note: this error originates in the macro `impl_ops` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0119`.
error: could not compile `uom` due to previous error

I'm a bit confused about how the default Sub impl conflicts when it seems the compiler doesn't have an implementation for Sub for thermodynamic temperatures.

The error in my previous comment was from an attempt to add a new Sub impl for ThermodynamicTemperature - TemperatureInterval. This new impl conflicts with the default Sub impl.

uom/src/system.rs

Lines 395 to 414 in 974449f

impl<D, Ul, Ur, V> $crate::lib::ops::$AddSubTrait<Quantity<D, Ur, V>>
for Quantity<D, Ul, V>
where
D: Dimension + ?Sized,
D::Kind: $crate::marker::$AddSubTrait,
Ul: Units<V> + ?Sized,
Ur: Units<V> + ?Sized,
V: $crate::num::Num + $crate::Conversion<V>,
{
type Output = Quantity<D, Ul, V>;
#[inline(always)]
fn $addsub_fun(self, rhs: Quantity<D, Ur, V>) -> Self::Output {
Quantity {
dimension: $crate::lib::marker::PhantomData,
units: $crate::lib::marker::PhantomData,
value: self.value $addsub_op change_base::<D, Ul, Ur, V>(&rhs.value),
}
}
}}

Why doesn't the default Sub impl work? There is some Rust subtlety here that I'm not seeing. Sorry! It seems odd that there's a conflicting implementation but that the compiler can't actually use that implementation for the operation on the given types.

The default impl is for any Quantity - Quantity where the quantity's dimension's kind supports add/sub (line 399). ThermodynamicTemperature and TemperatureInterval are just type aliases for Quantity so they are covered by this default impl. However, because ThermodynamicTemperature's kind does not support add/sub the default impl does not take effect. A new specialized impl can't be added because the type aliases boil down to the same types in the default impl. If there was some way to add a negative trait bound then this could be used to make the two disjoint.