uom

repository·master·Indexed 22 days ago

https://github.com/iliekturtles/uom

A Rust crate for automatic, type-safe, zero-cost dimensional analysis. It allows developers to work with physical quantities (such as length, mass, and time) instead of raw numbers to prevent unit-mismatch errors at compile time. The library supports the International System of Units (SI) and provides a wide range of storage types including floating-point, integers, rationals, and complex numbers. It also includes uom-macros for defining custom systems of quantities and units.

Tokens
3.8K
Snippets
7
Records
14
Agent score
77%

What's inside uom

  1. Use uom-macros for procedural macro support

    master

    The uom-macros crate provides procedural macro support for the uom ecosystem. It offers two primary function-style macros to simplify the definition of physical quantities and units:

    1. system!: Used to define a complete system of quantities and a related system of units.
    2. quantity!: Used to define specific quantities within an existing system.

    Note: This crate is currently a placeholder for future development. For core functionality and full details on how to use units and quantities, refer to the main uom crate.

  2. How uom's dimensional analysis works

    master

    Core Abstraction: Quantities vs Units

    uom operates on quantities (e.g., Length, Mass, Time) rather than individual units (e.g., meters, kilograms, seconds).

    Mental Model:

    1. Interface Boundaries: Units are primarily used at the boundaries of your code (when creating a quantity or extracting a value).
    2. Internal Normalization: Internally, uom normalizes all values to the base unit of that quantity. For example, if the base unit for Length is meter, a value of 1 kilometer is stored internally as 1000.0 meters.
    3. Zero-Cost Abstraction: Because operations are performed on the normalized base units, arithmetic operations like +, -, *, and / have zero runtime cost compared to using the raw underlying storage type (like f32).
    4. Type Safety: The compiler prevents invalid operations (e.g., adding Length to Time) because they represent different quantities.

    Important Precision Warning

    Since uom normalizes to a base unit, you must be aware of the limitations of your chosen underlying storage type. If you use an integer type (like i32) for a quantity where the base unit is meter, you cannot represent sub-meter values (like 1 centimeter) because 0.01 cannot be stored in an integer. Always choose a storage type that can accommodate the precision required by your base units.

  3. Configure uom Cargo features

    master

    You can control the available storage types, the inclusion of the SI system, and other capabilities using Cargo features. By default, f32, f64, std, and si are enabled.

    To customize your build, use --no-default-features and specify the required features in Cargo.toml:

    [dependencies]
    uom = {
        version = "0.38.0",
        default-features = false,
        features = [
            "autoconvert", 
            "f32", 
            "si", 
            "std",
            "serde",
        ]
    }

    Available Features

    • autoconvert: Enables automatic conversion between base units in binary operators. Disabling this requires quantities to share the same base units for direct interaction.
    • Storage Types: At least one storage type feature must be enabled. Options include:
      • Floating point: f32, f64 (default)
      • Unsigned integers: usize, u8, u16, u32, u64, u128
      • Signed integers: isize, i8, i16, i32, i64, i128
      • Arbitrary width: bigint, biguint
      • Rational: rational, rational32, rational64, bigrational
      • Complex: complex32, complex64
    • si: Includes the pre-built International System of Units (enabled by default).
    • std: Enables standard library support (enables no_std if disabled).
    • serde: Enables serialization and deserialization support (disabled by default).
    [dependencies]
    uom = {
        version = "0.38.0",
        default-features = false,
        features = [
            "autoconvert", # automatic base unit conversion.
            "usize", "u8", "u16", "u32", "u64", "u128", # Unsigned integer storage types.
            "isize", "i8", "i16", "i32", "i64", "i128", # Signed integer storage types.
            "bigint", "biguint", # Arbitrary width integer storage types.
            "rational", "rational32", "rational64", "bigrational", # Integer ratio storage types.
            "complex32", "complex64", # Complex floating point storage types.
            "f32", "f64", # Floating point storage types.
            "si", "std", # Built-in SI system and std library support.
            "serde", # Serde support.
        ]
    }
  4. Use quantities and units in Rust

    master

    The uom crate provides type-safe dimensional analysis by working with quantities (e.g., Length, Time) rather than individual units.

    Key patterns:

    • Creation: Use Quantity::new::<unit>(value) to create a quantity in a specific unit.
    • Operations: Perform arithmetic (addition, subtraction, multiplication, division) directly on quantities. The library ensures dimensional correctness at compile time.
    • Conversion: Use .get::<unit>() to retrieve the value of a quantity in a different unit.

    Example usage with the SI system:

    extern crate uom;
    
    use uom::si::f32::*;
    use uom::si::length::kilometer;
    use uom::si::time::second;
    
    fn main() {
        let length = Length::new::<kilometer>(5.0);
        let time = Time::new::<second>(15.0);
        let velocity/*: Velocity*/ = length / time;
        let _acceleration = calc_acceleration(velocity, time);
        //let error = length + time; // error[E0308]: mismatched types
    
        // Get a quantity value in a specific unit.
        let time_in_nano_seconds = time.get::<uom::si::time::nanosecond>();
    }
    
    fn calc_acceleration(velocity: Velocity, time: Time) -> Acceleration {
        velocity / time
    }
  5. Configure uom features

    master

    You can customize uom using Cargo features. By default, f32, f64, si, and std are enabled. Use --no-default-features to cherry-pick specific capabilities.

    Available Features

    FeatureDescription
    autoconvertEnables automatic conversion between base units in binary operators.
    siIncludes the pre-built International System of Units (SI).
    stdEnables standard library support (disable for no_std environments).
    serdeEnables serialization/deserialization support via the serde crate.
    f32, f64Enables floating-point storage types (enabled by default).
    u8...u128, isize...i128Enables various integer storage types.
    bigint, biguintEnables arbitrary-width integer storage.
    rational, rational32, rational64, bigrationalEnables rational number storage.
    complex32, complex64Enables complex floating-point storage.

    Note: At least one underlying storage type feature (e.g., f32, i32, etc.) must be enabled.

    [dependencies]
    uom = {
        version = "0.38.0",
        default-features = false,
        features = [
            "autoconvert",
            "f32",
            "si",
            "std",
        ]
    }
  6. Basic usage of quantities and units

    master

    The uom crate provides type-safe dimensional analysis. You work with quantities (e.g., Length, Time) rather than raw units. Units are used at the boundaries to create quantities or to extract values in specific units. Operations like addition and multiplication are checked at compile time to prevent invalid dimensional operations (e.g., adding Length to Time).

    Example using the SI system with f32 storage:

    use uom::si::f32::*;
    use uom::si::length::kilometer;
    use uom::si::time::second;
    
    fn main() {
        // Create quantities using specific units
        let length = Length::new::<kilometer>(5.0);
        let time = Time::new::<second>(15.0);
    
        // Perform dimensional analysis (Length / Time = Velocity)
        let velocity: Velocity = length / time;
    
        // Get a quantity value in a specific unit
        let time_in_nano_seconds = time.get::<uom::si::time::nanosecond>();
    }
    use uom::si::f32::*;
    use uom::si::length::kilometer;
    use uom::si::time::second;
    
    fn main() {
        let length = Length::new::<kilometer>(5.0);
        let time = Time::new::<second>(15.0);
        let velocity/*: Velocity*/ = length / time;
        let _acceleration = calc_acceleration(velocity, time);
        //let error = length + time; // error[E0308]: mismatched types
    
        // Get a quantity value in a specific unit.
        let time_in_nano_seconds = time.get::<uom::si::time::nanosecond>();
    }
    
    fn calc_acceleration(velocity: Velocity, time: Time) -> Acceleration {
        velocity / time
    }
  7. Convert between Time and Duration

    master

    You can convert between the Time quantity and the Duration type using the TryFrom trait.

    Conversion Precision

    • Time to Duration: Accurate to within 1 nanosecond (subject to floating point rounding).
    • Duration to Time: Accurate to within 100 nanoseconds (subject to floating point rounding).

    Error Handling

    Conversions may fail with a TryFromError:

    • TryFromError::NegativeDuration: Occurs when attempting to convert a negative Time interval into a Duration.
    • TryFromError::Overflow: Occurs when the value exceeds the capacity of the target type (e.g., Duration's internal storage).

    To handle negative time intervals, use .abs() on the Time value before conversion.

  8. Available Time units and quantities

    master

    The Time quantity uses the second (s) as its base SI unit. It supports a wide range of SI prefixes (from yoctoseconds to yottaseconds) and several non-SI units for common time intervals.

    SI Prefixed Units

    • ys (yoctosecond) to Ys (yottasecond)
    • ms (millisecond), ns (nanosecond), µs (microsecond), etc.

    Common Time Units

    • s (second)
    • min (minute)
    • h (hour)
    • d (day)
    • shake (1.0e-8 s)
    • a (year)
    • a (tropical) (tropical year)
    • a (sidereal) (sidereal year)

    Sidereal Units

    • s (sidereal) (sidereal second)
    • d (sidereal) (sidereal day)
    • h (sidereal) (sidereal hour)
    • a (sidereal) (sidereal year)
    @second: prefix!(none); "s", "second", "seconds";
    @day: 8.64_E4; "d", "day", "days";
    @hour: 3.6_E3; "h", "hour", "hours";
    @minute: 6.0_E1; "min", "minute", "minutes";
    @shake: 1.0_E-8; "10.0 ns", "shake", "shakes";
    @year: 3.1536_E7; "a", "year", "years";
    @second_sidereal: 9.972_696_E-1; "s (sidereal)", "second (sidereal)", "seconds (sidereal)";
    @day_sidereal: 8.616_409_E4; "d (sidereal)", "day (sidereal)", "days (sidereal)";
    @hour_sidereal: 3.590_170_E3; "h (sidereal)", "hour (sidereal)", "hours (sidereal)";
    @year_sidereal: 3.155_815_E7; "a (sidereal)", "year (sidereal)", "years (sidereal)";
    @year_tropical: 3.155_693_E7; "a (tropical)", "year (tropical)", "years (tropical)";
  9. Parse quantity errors

    master

    When parsing strings into a Quantity using the uom::str module, the following errors may be returned via ParseQuantityError:

    • NoSeparator: No space was found between the quantity value and the units.
    • ValueParseError: An error occurred while parsing the numeric value portion of the string.
    • UnknownUnit: The unit abbreviation provided was not recognized for that quantity.
  10. TryFromError for Time conversions

    master

    The TryFromError enum defines the failure modes when converting between Time and Duration.

    • NegativeDuration: The given time interval was negative, making conversion to a duration nonsensical. To convert a negative time interval to a duration, first use abs to make it positive.
    • Overflow: The given time interval exceeded the maximum size of a Duration.
    #[derive(Debug, Clone, Copy)]
    pub enum TryFromError {
        NegativeDuration,
        Overflow,
    }
  11. Reference: uom Cargo features

    master

    The following features are available for controlling storage types and functionality in uom:

    autoconvert, # automatic base unit conversion.
    # Unsigned integer storage types
    u8, u16, u32, u64, u128, usize,
    # Signed integer storage types
    i8, i16, i32, i64, i128, isize,
    # Arbitrary width integer storage types
    bigint, biguint,
    # Integer ratio storage types
    rational, rational32, rational64, bigrational,
    # Complex floating point storage types
    complex32, complex64,
    # Floating point storage types
    f32, f64,
    # System and library support
    si, std, serde