iliekturtles / uom

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

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unclear about how to create arbitrary quantities with arbitrary units

joe-saronic opened this issue · comments

commented

I'm new to rust and very new to UOM, so admittedly, this is likely user error caused by a lack of understanding. This is a followup from my StackOverflow post, which got no traction unfortunately.

I am trying to make a simple type that will be backed by an array in the default units of a given dimension. The idea is pretty simple: I provide a constructor that converts quantities to base units and getters that convert them back to desired units for some given dimension:

use anyhow::Result;
use core::marker::PhantomData;
use uom::{
    Conversion, si::{Dimension, Quantity, SI, Unit, length::{Dimension as Length, meter}},
};

#[derive(Debug)]
struct Coordinate<D: Dimension + ?Sized> {
    buf: [f64; 2],
    dim: PhantomData<D>,
}

impl<D: Dimension + ?Sized> Coordinate<D>
{
    pub fn new<U: Unit + Conversion<f64>>(x: f64, y: f64) -> Self {
        Self {
            buf: [
                Quantity::<D, SI<f64>, f64>::new::<U>(x).value,
                Quantity::<D, SI<f64>, f64>::new::<U>(y).value,
            ],
            dim: PhantomData,
        }
    }

    pub fn x(&self) -> Quantity<D, SI<f64>, f64> {
        Quantity::<D, SI<f64>, f64> { dimension: PhantomData, units: PhantomData, value: self.buf[0], }
    }

    pub fn y(&self) -> Quantity<D, SI<f64>, f64> {
        Quantity::<D, SI<f64>, f64> { dimension: PhantomData, units: PhantomData, value: self.buf[1], }
    }
}

fn main() -> Result<()> {
    let n = Coordinate::<Length>::new::<meter>(1.0, 1.0);
    println!("{n:?}");
    Ok(())
}

I end up with a couple of different errors that I can't quite make sense of:

   Compiling scratchpad v0.1.0 (/home/joe/saronic/scratch/scratchpad)
error[E0599]: no function or associated item named `new` found for struct `Quantity<D, ..., ...>` in the current scope
  --> src/main.rs:18:46
   |
18 |                 Quantity::<D, SI<f64>, f64>::new::<U>(x).value,
   |                                              ^^^ function or associated item not found in `Quantity<D, ..., ...>`
   |
   = note: the full type name has been written to '/home/joe/saronic/scratch/scratchpad/target/debug/deps/scratchpad-dd83b867192bd049.long-type-8409603177462832417.txt'
   = note: the function or associated item was found for
           - `Quantity<(dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UTerm, B1>>, M = Z0, N = Z0, T = PInt<UInt<UTerm, B1>>, Th = Z0> + 'static), U, V>`
           - `Quantity<(dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UTerm, B1>>, M = Z0, N = Z0, T = NInt<UInt<UInt<UTerm, B1>, B0>>, Th = Z0> + 'static), U, V>`
           - `Quantity<(dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UInt<UTerm, B1>, B0>>, M = PInt<UInt<UTerm, B1>>, N = Z0, T = NInt<UInt<UTerm, B1>>, Th = Z0> + 'static), U, V>`
           - `Quantity<(dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = Z0, M = Z0, N = PInt<UInt<UTerm, B1>>, T = Z0, Th = Z0> + 'static), U, V>`
           and 106 more types

error[E0599]: no function or associated item named `new` found for struct `Quantity<D, ..., ...>` in the current scope
  --> src/main.rs:19:46
   |
19 |                 Quantity::<D, SI<f64>, f64>::new::<U>(y).value,
   |                                              ^^^ function or associated item not found in `Quantity<D, ..., ...>`
   |
   = note: the full type name has been written to '/home/joe/saronic/scratch/scratchpad/target/debug/deps/scratchpad-dd83b867192bd049.long-type-8409603177462832417.txt'
   = note: the function or associated item was found for
           - `Quantity<(dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UTerm, B1>>, M = Z0, N = Z0, T = PInt<UInt<UTerm, B1>>, Th = Z0> + 'static), U, V>`
           - `Quantity<(dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UTerm, B1>>, M = Z0, N = Z0, T = NInt<UInt<UInt<UTerm, B1>, B0>>, Th = Z0> + 'static), U, V>`
           - `Quantity<(dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UInt<UTerm, B1>, B0>>, M = PInt<UInt<UTerm, B1>>, N = Z0, T = NInt<UInt<UTerm, B1>>, Th = Z0> + 'static), U, V>`
           - `Quantity<(dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = Z0, M = Z0, N = PInt<UInt<UTerm, B1>>, T = Z0, Th = Z0> + 'static), U, V>`
           and 106 more types

error[E0277]: `dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UTerm, B1>>, M = Z0, N = Z0, T = Z0, Th = Z0>` doesn't implement `Debug`
  --> src/main.rs:36:15
   |
36 |     println!("{n:?}");
   |               ^^^^^ `dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UTerm, B1>>, M = Z0, N = Z0, T = Z0, Th = Z0>` cannot be formatted using `{:?}` because it doesn't implement `Debug`
   |
   = help: the trait `Debug` is not implemented for `dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UTerm, B1>>, M = Z0, N = Z0, T = Z0, Th = Z0>`
   = help: the following other types implement trait `Debug`:
             (dyn Any + 'static)
             (dyn Any + Send + 'static)
             (dyn Any + Send + Sync + 'static)
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `scratchpad` (bin "scratchpad") due to 3 previous errors

I am having trouble understanding what is going on here because I was under the impression that there is a blanket implementation of new for Quantity, but apparently that's not the case.

If I comment out the lines with Quantity::<D, SI<f64>, f64>::new::<U>(...).value,, e.g. like 0.0, //Quantity::<D, SI<f64>, f64>::new::<U>(x).value,, the debug error still persists:

   Compiling scratchpad v0.1.0 (/home/joe/saronic/scratch/scratchpad)
error[E0277]: `dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UTerm, B1>>, M = Z0, N = Z0, T = Z0, Th = Z0>` doesn't implement `Debug`
  --> src/main.rs:36:15
   |
36 |     println!("{n:?}");
   |               ^^^^^ `dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UTerm, B1>>, M = Z0, N = Z0, T = Z0, Th = Z0>` cannot be formatted using `{:?}` because it doesn't implement `Debug`
   |
   = help: the trait `Debug` is not implemented for `dyn Dimension<I = Z0, J = Z0, Kind = (dyn Kind + 'static), L = PInt<UInt<UTerm, B1>>, M = Z0, N = Z0, T = Z0, Th = Z0>`
   = help: the following other types implement trait `Debug`:
             (dyn Any + 'static)
             (dyn Any + Send + 'static)
             (dyn Any + Send + Sync + 'static)
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.
error: could not compile `scratchpad` (bin "scratchpad") due to previous error

I have had a hard time finding a good tutorial for UOM. My issue is that while the crate is well documented and thorough, it appears to assume a certain level of prior knowledge that I simply don't have.

I didn't have a chance to fully go through your code and try to compile locally to resolve the errors. My guess right now is that type constraints are missing and compiler isn't explicitly calling them out.

Is there any reason you want to store the data as buf: [f64; 2], and not buf: [Quantity<D, SI<f64>, f64>; 2],? Doing this would skip the need to construct and deconstruct the types and reduce the need for constraints.

commented

@iliekturtles Thanks for looking into this. The arrays in this code are a standin for nalgebra::Vector2, which is what I'm really using. I didn't think the particular type was related to the issue, but I'll definitely try to make a vector of quantities.

commented

@iliekturtles That being said, is there a way to convert an f64 and a Units to a quantity of the appropriate dimension somehow? It seems like this should be possible, but I haven't had any luck figuring it out.

commented

I have had a hard time understanding how to get Dimension from a Units, and how the Conversion trait really works for arbitrary cases. I suspect that it's tied to the answer somehow.

If you know the quantity (Length in the example below), you can explicitly provide the dimension, underlying storage type, and unit to create a quantity. There isn't a way to get the dimension when you just have a unit.

use uom::si::length as l;
use uom::si::*;

fn main() {
    let x = Quantity::<l::Dimension, SI<f32>, f32>::new::<l::meter>(5.0);

    println!("{:?}", x); // 5.0 m^1
}
commented

The issue is that I don't have the quantity available initially. Is there a way to get the mapping between meter and length::Dimension somehow?