Can ncollide work with units of measure?

The uom crate essentially raises physical units to the Rust type level. That is, it prevents you from confusing, say, mass with length.

Can ncollide be sensibly made to work with uom types? I’m guessing that this would involve implementing nalgebra::RealField for the uom types. In which case I see two immediate problems

  • Orphan rules (unless done within uom or nalgebra)
  • It would be veeeery repetitive, unless there is some meta-programming support for implementing RealField.

Has anyone thought about this before?

Hmm, it seems that I must have made a mistake when I tried this earlier. It appears to be working now. I’ll have to do some more extensive testing when I have the time, but it’s looking good at the moment!

It turns out it’s a bit more subtle: It works up to a point.

Here is an example which demonstrates that making nc::math::{Vector, Point} and subtracting the latter, works just as well with uom::Length as it does with f32.

However, Vector::norm() does not work with uom::Length (no problem with f32).

Sample code

use uom::si::f32::Length;
use uom::si::length::meter;
use ncollide3d as nc;
use nc::math::{Vector, Point};

fn  m(x: f32) -> Length { Length::new::<meter> (x) }

type PL = nc::math::Point<Length>;
type Pf = nc::math::Point<f32>;

fn main() {

    let p1f: Point<f32>    = Pf::new(  1.2,    1.3,    1.4);
    let p2f                = Pf::new(  2.1,    3.1,    4.1);

    let p1u: Point<Length> = PL::new(m(1.2), m(1.3), m(1.4));
    let p2u                = PL::new(m(2.1), m(3.1), m(4.1));

    let vf: Vector<f32>    = p2f - p1f;
    let vu: Vector<Length> = p2u - p1u;

    println!("{:?}", p2u - p1u);
    println!("{:?}", p2f - p1f);

    println!("{:?}", vf);
    println!("{:?}", vu);

    #[cfg(feature = "show-compiler-error")]
    println!("{:?}", vu.norm());
    println!("{:?}", vf.norm());
}

Error message digest

The most relevant part of the compiler error is probably

doesn't satisfy `<_ as ncollide3d::nalgebra::SimdComplexField>::SimdRealField = _`
doesn't satisfy `_: ncollide3d::nalgebra::SimdComplexField`

Question

Can I do anything to get around this problem, in order to be able to use .norm() on nc::math::Vector<uom::si::f32::Length>?

Full error message

For completeness, the (very noisy) full compiler error message is

error[E0599]: no method named `norm` found for struct `ncollide3d::nalgebra::Matrix<uom::si::Quantity<(dyn uom::si::Dimension<N = uom::typenum::Z0, T = uom::typenum::Z0, Kind = (dyn uom::Kind + 'static), L = uom::typenum::PInt<uom::typenum::UInt<uom::typenum::UTerm, uom::typenum::B1>>, M = uom::typenum::Z0, J = uom::typenum::Z0, Th = uom::typenum::Z0, I = uom::typenum::Z0> + 'static), (dyn uom::si::Units<f32, mass = uom::si::mass::kilogram, luminous_intensity = uom::si::luminous_intensity::candela, thermodynamic_temperature = uom::si::thermodynamic_temperature::kelvin, electric_current = uom::si::electric_current::ampere, length = uom::si::length::meter, amount_of_substance = uom::si::amount_of_substance::mole, time = uom::si::time::second> + 'static), f32>, ncollide3d::nalgebra::U3, ncollide3d::nalgebra::U1, ncollide3d::nalgebra::ArrayStorage<uom::si::Quantity<(dyn uom::si::Dimension<N = uom::typenum::Z0, T = uom::typenum::Z0, Kind = (dyn uom::Kind + 'static), L = uom::typenum::PInt<uom::typenum::UInt<uom::typenum::UTerm, uom::typenum::B1>>, M = uom::typenum::Z0, J = uom::typenum::Z0, Th = uom::typenum::Z0, I = uom::typenum::Z0> + 'static), (dyn uom::si::Units<f32, mass = uom::si::mass::kilogram, luminous_intensity = uom::si::luminous_intensity::candela, thermodynamic_temperature = uom::si::thermodynamic_temperature::kelvin, electric_current = uom::si::electric_current::ampere, length = uom::si::length::meter, amount_of_substance = uom::si::amount_of_substance::mole, time = uom::si::time::second> + 'static), f32>, ncollide3d::nalgebra::U3, ncollide3d::nalgebra::U1>>` in the current scope
   --> examples/playground.rs:29:25
    |
29  |       println!("{:?}", vu.norm());
    |                           ^^^^ method not found in `ncollide3d::nalgebra::Matrix<uom::si::Quantity<(dyn uom::si::Dimension<N = uom::typenum::Z0, T = uom::typenum::Z0, Kind = (dyn uom::Kind + 'static), L = uom::typenum::PInt<uom::typenum::UInt<uom::typenum::UTerm, uom::typenum::B1>>, M = uom::typenum::Z0, J = uom::typenum::Z0, Th = uom::typenum::Z0, I = uom::typenum::Z0> + 'static), (dyn uom::si::Units<f32, mass = uom::si::mass::kilogram, luminous_intensity = uom::si::luminous_intensity::candela, thermodynamic_temperature = uom::si::thermodynamic_temperature::kelvin, electric_current = uom::si::electric_current::ampere, length = uom::si::length::meter, amount_of_substance = uom::si::amount_of_substance::mole, time = uom::si::time::second> + 'static), f32>, ncollide3d::nalgebra::U3, ncollide3d::nalgebra::U1, ncollide3d::nalgebra::ArrayStorage<uom::si::Quantity<(dyn uom::si::Dimension<N = uom::typenum::Z0, T = uom::typenum::Z0, Kind = (dyn uom::Kind + 'static), L = uom::typenum::PInt<uom::typenum::UInt<uom::typenum::UTerm, uom::typenum::B1>>, M = uom::typenum::Z0, J = uom::typenum::Z0, Th = uom::typenum::Z0, I = uom::typenum::Z0> + 'static), (dyn uom::si::Units<f32, mass = uom::si::mass::kilogram, luminous_intensity = uom::si::luminous_intensity::candela, thermodynamic_temperature = uom::si::thermodynamic_temperature::kelvin, electric_current = uom::si::electric_current::ampere, length = uom::si::length::meter, amount_of_substance = uom::si::amount_of_substance::mole, time = uom::si::time::second> + 'static), f32>, ncollide3d::nalgebra::U3, ncollide3d::nalgebra::U1>>`
    | 
   ::: /tmp/testing/.cargo/registry/src/github.com-1ecc6299db9ec823/uom-0.30.0/src/si/mod.rs:10:1
    |
10  | / system! {
11  | |     /// [International System of Quantities](http://jcgm.bipm.org/vim/en/1.6.html) (ISQ).
12  | |     ///
13  | |     /// # Generic Parameters
...   |
102 | |     }
103 | | }
    | | -
    | | |
    | |_doesn't satisfy `<_ as ncollide3d::nalgebra::SimdComplexField>::SimdRealField = _`
    |   doesn't satisfy `_: ncollide3d::nalgebra::SimdComplexField`
    |
    = note: the method `norm` exists but the following trait bounds were not satisfied:
            `uom::si::Quantity<dyn uom::si::Dimension<N = uom::typenum::Z0, T = uom::typenum::Z0, Kind = (dyn uom::Kind + 'static), L = uom::typenum::PInt<uom::typenum::UInt<uom::typenum::UTerm, uom::typenum::B1>>, M = uom::typenum::Z0, J = uom::typenum::Z0, Th = uom::typenum::Z0, I = uom::typenum::Z0>, dyn uom::si::Units<f32, mass = uom::si::mass::kilogram, luminous_intensity = uom::si::luminous_intensity::candela, thermodynamic_temperature = uom::si::thermodynamic_temperature::kelvin, electric_current = uom::si::electric_current::ampere, length = uom::si::length::meter, amount_of_substance = uom::si::amount_of_substance::mole, time = uom::si::time::second>, f32>: ncollide3d::nalgebra::SimdComplexField`
            `<uom::si::Quantity<dyn uom::si::Dimension<N = uom::typenum::Z0, T = uom::typenum::Z0, Kind = (dyn uom::Kind + 'static), L = uom::typenum::PInt<uom::typenum::UInt<uom::typenum::UTerm, uom::typenum::B1>>, M = uom::typenum::Z0, J = uom::typenum::Z0, Th = uom::typenum::Z0, I = uom::typenum::Z0>, dyn uom::si::Units<f32, mass = uom::si::mass::kilogram, luminous_intensity = uom::si::luminous_intensity::candela, thermodynamic_temperature = uom::si::thermodynamic_temperature::kelvin, electric_current = uom::si::electric_current::ampere, length = uom::si::length::meter, amount_of_substance = uom::si::amount_of_substance::mole, time = uom::si::time::second>, f32> as ncollide3d::nalgebra::SimdComplexField>::SimdRealField = _`