Expand description
Compile-time dimensional analysis for various unit systems using Rust’s type system.
Its goal is to provide zero cost unit safety while requiring minimal effort from the programmer.
For a short introduction and some examples, check out the readme on GitHub.
§Use
The recommended way to import this crate is with the rename
extern crate dimensioned as dim;
and then either import the module for the unit system you will be using,
use dim::si;
let x = 3.0 * si::M;
or import the constants and/or types that you care about,
use dim::si::{Meter, M, S};
let x: Meter<f64> = 3.0 * M;
let t = 2.0 * S;
For clarity, all the examples contained herein will import unit system modules.
§How it works
When a unit system is created, say the SI system, a struct with two parameters is made; in this
case SI<V, U>
. The first parameter, V
, is for the value type – it can be anything, and is the
value to which we are giving units. The second parameter, U
, is where the magic happens. It
represents the units, and is a type-level array of type-level numbers. Now, these guys have some
pretty ugly type signatures, so I will express the array as the macro which maps to it, tarr![]
,
and the type numbers as their aliases; N1
for -1, Z0
for 0, P1
for 1, etc.
The SI system has seven base units, so that’s how many elements the type array needs to have. The
order is the order in which the base units were defined, and the values of the type numbers are the
power to which each unit is raised. For example, the first three SI units, in order, are Meter
,
Kilogram
, Second
, so the following aliases exist:
#[macro_use]
extern crate dimensioned as dim;
use dim::si::SI;
use dim::typenum::{P1, Z0, N1, N2};
type Meter<V> = SI<V, tarr![P1, Z0, Z0, Z0, Z0, Z0, Z0]>;
type Kilogram<V> = SI<V, tarr![Z0, P1, Z0, Z0, Z0, Z0, Z0]>;
type Second<V> = SI<V, tarr![Z0, Z0, P1, Z0, Z0, Z0, Z0]>;
type Newton<V> = SI<V, tarr![P1, P1, N2, Z0, Z0, Z0, Z0]>;
// ...
fn main() {}
In addition to creating the unit system struct, SI
, the type aliases, and constants for each,
various traits are implemented for the unit system. These include arithmetic operations, which work
as follows.
If you try to add a Meter<f64>
to a Second<f64>
, then you will get a compiler error because
they have different types and so addition is not defined. Multiplication, on the other hand, is
defined, and results in a normal multiplication of the value types, and the unit powers added.
So, multiplying Meter<f64>
by Second<f64>
gives SI<f64, tarr![P1, Z0, P1, Z0, Z0, Z0, Z0]>
.
§Example
extern crate dimensioned as dim;
use dim::si;
fn main() {
let x = 3.0 * si::M;
let t = 2.0 * si::S;
assert_eq!("6 m*s", &format!("{}", x*t));
}
That’s basically it. All of the dimensional safety comes from whether things typecheck, and from performing type-level arithmetic, thanks to the typenum crate. Pretty much everything else is for ergonomics.
Re-exports§
pub extern crate generic_array;
pub extern crate typenum;
pub use crate::unit_systems::cgs;
pub use crate::unit_systems::fps;
pub use crate::unit_systems::mks;
pub use crate::unit_systems::si;
pub use crate::unit_systems::ucum;
pub use crate::traits::*;
Modules§
- array
- Tools for converting from type arrays of type numbers to generic arrays.
- conversion
- Conversion between unit systems
- dimensions
- Marker traits for various dimensions
- f32prefixes
- Constants for all SI prefixes as
f32
s - f64prefixes
- Constants for all SI prefixes as
f64
s - traits
- Traits for working generically with dimensioned
- unit_
systems - Predefined unit systems
Macros§
- derived
- Create a derived unit based on existing ones
- make_
units - Create a new unit system
- tarr
- Construct a type-level array of type-level integers