uom
uom copied to clipboard
How to use type aliases for new/combined quantities
To make a new combined Quantity representing a change in mass concentration per time, that works with SI, one can do something like:
type MassConcentrationRate = Quantity<uom::si::ISQ<N3, P1, N1, Z0, Z0, Z0, Z0>, uom::si::SI<f64>, f64>;
That's fine. From there the alias works in function signatures alongside existing uom quantities, like:
/// Calculate the frequency of mass concentration change
pub fn calculate_frequency (
c_x: &MassConcentration,
d_cx_dt: &MassConcentrationRate,
) -> Frequency {
*d_cx_dt / *c_x
}
But to instantiate that type (EDIT: while maintaining unit checking), it's necessary to use other types; as so:
let d_cx_dt: MassConcentrationRate
= MassConcentration::new::<kilogram_per_liter>(30.0) * Frequency::new::<hertz>(1.0);
Then it can be used:
let c_x = MassConcentration::new::<kilogram_per_liter>(30.0);
let freq = calculate_frequency(&c_x, &d_cx_dt);
This all works fine, but:
- Is it the idomatic/intended way to accomplish this? EDIT: It seems like even though one can use the Quantity struct definition with fields, that feels like going against the point.
- Is it possible to use the
quantity
macro without defining a new entire system of units? That did not seem to work. - Should PRs even be opened to add in new quantities like this whose use case might not be common?
- This is probably the best way currently without adding a new quantity. The
*
between should be optimized away at compile time and it's likely more clear than using the type constructor:let d_cx_dt = MassConcentrationRate { dimension: PhantomData, units: PhantomData, value: 30.0, };
as this method implicitly uses the base units. - The
quantity!
macro can't be used outside thesystem!
macro currently. I attempted to do this last year and ran into a couple roadblocks and stopped. - PRs, even for uncommon quantities, are always appreciated.
If defining a custom type alias in this way, what's the best way to go about defining a unit for the new quantity type?
I tried using the unit!
macro, but I get the error unresolved import [my-new-type-quantity
--> orbits/src/units.rs:5:1
|
5 | / unit! {
6 | | system: uom::si;
7 | | quantity: GravitationalParamter;
8 | |
9 | | @meters_cubed_per_seconds_squared: 1.0; "m³ / s²", "", "";
10 | | }
| |_^ no external crate `GravitationalParamter`
|
= note: this error originates in the macro `unit` (in Nightly builds, run with -Z macro-backtrace for more info)
at this point am I better off defining my own system?
Is there a guide or example somewhere on how to extend your SI system without needing to redefine the entire thing with my additions?
I found an extremely stupid hack/workaround. I defined a const
pub const ONE_METER_CUBED_PER_SECOND_SQUARED: GravitationalParameter = (Length::new::<meter>(1.0)).powi(P3::new()) * (Time::new::<second>(1.0)).powi(N2::new());
let mu: GravitationalParamter = 3.986e14 * ONE_METER_CUBED_PER_SECOND_SQUARED;
but, I mean, I kind of hate that. Surely there's a better way.
Right?
@mia-n FWIW I haven't looked at this in a while, and I forgot to close the issue.
If defining a custom type alias in this way, what's the best way to go about defining a unit for the new quantity type?
I tried using the
unit!
macro, but I get the errorunresolved import [my-new-type-quantity
--> orbits/src/units.rs:5:1 | 5 | / unit! { 6 | | system: uom::si; 7 | | quantity: GravitationalParamter; 8 | | 9 | | @meters_cubed_per_seconds_squared: 1.0; "m³ / s²", "", ""; 10 | | } | |_^ no external crate `GravitationalParamter` | = note: this error originates in the macro `unit` (in Nightly builds, run with -Z macro-backtrace for more info)
It seems like the macro invocation should work, but there might just be a typo: GravitationalParamter
≠ GravitationalParameter
.
I found an extremely stupid hack/workaround. I defined a const
pub const ONE_METER_CUBED_PER_SECOND_SQUARED: GravitationalParameter = (Length::new::<meter>(1.0)).powi(P3::new()) * (Time::new::<second>(1.0)).powi(N2::new()); let mu: GravitationalParamter = 3.986e14 * ONE_METER_CUBED_PER_SECOND_SQUARED;
but, I mean, I kind of hate that. Surely there's a better way.
Right?
This workaround could be rewritten as a macro to make it easier if you have to do it a lot. However, I don't know if that's any better or less effort than using the unit
macro if you can get that working.
Ah that was a silly mistake when re-writing this to take a picture, thank you for catching that. With it fixed, I do still get almost (but not quite) the same error:
error[E0432]: unresolved import `__quantity`
--> orbits/src/units.rs:11:1
|
11 | / unit! {
12 | | system: uom::si;
13 | | quantity: GravitationalParameter;
14 | |
15 | | @meter_cubed_per_second_squared: prefix!(none);
16 | | "m³ / s²", "meter cubed per second squared", "meters cubed per second squared";
17 | | }
| |_^ `__quantity` is a type alias, not a module
|
= note: this error originates in the macro `unit` (in Nightly builds, run with -Z macro-backtrace for more info)
I think it's because unit (here) is expecting quantity to be a module? I'm not super sure, I'm kind of new to rust and stumbling my way through this.
I think it's because unit (here) is expecting quantity to be a module? I'm not super sure, I'm kind of new to rust and stumbling my way through this.
@mia-n If you check here maybe you can use the quantity
macro to implement the GravitationalParameter quantity?
The documentation for the unit
macro says you're on the right track that quantity is looking for a path to the module where quantity
was invoked.
I'm not sure from your use case whether you want a unit or a quantity here.
Also, FYI sometimes if you're stuck on a macro invocation, you can use cargo-expand
, but it needs nightly rust to be installed too.