{"id":20523914,"url":"https://github.com/yoric/yaiouom","last_synced_at":"2025-04-13T08:18:18.339Z","repository":{"id":46923222,"uuid":"127565643","full_name":"Yoric/yaiouom","owner":"Yoric","description":"Prototype extension of the Rust type system towards checking units-of-measure","archived":false,"fork":false,"pushed_at":"2020-04-14T07:34:50.000Z","size":1102,"stargazers_count":115,"open_issues_count":3,"forks_count":7,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-13T08:18:03.316Z","etag":null,"topics":["rust","types","units-of-measure"],"latest_commit_sha":null,"homepage":"https://yoric.github.io/yaiouom/yaiouom/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Yoric.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-31T19:47:18.000Z","updated_at":"2025-03-12T16:11:52.000Z","dependencies_parsed_at":"2022-08-31T11:14:12.245Z","dependency_job_id":null,"html_url":"https://github.com/Yoric/yaiouom","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yoric%2Fyaiouom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yoric%2Fyaiouom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yoric%2Fyaiouom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yoric%2Fyaiouom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Yoric","download_url":"https://codeload.github.com/Yoric/yaiouom/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248681519,"owners_count":21144703,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rust","types","units-of-measure"],"created_at":"2024-11-15T22:46:07.501Z","updated_at":"2025-04-13T08:18:18.305Z","avatar_url":"https://github.com/Yoric.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"Units of measure.\n\nThis crate implements a mechanism of units of measure.\nIt may be used to manipulate all sorts of measures,\nincluding physics/engineering (m, kg, s, A, m * s ^ 1,\n...), currencies (EUR, USD, ...), statistics (dollars\nper barrel, engineers per lightbulb, dollars per household\nper year, ...)\n\nWhile this is not the first implementation of units of\nmeasure in Rust, this is the first one that is both\nextensible (you can trivially add new base units),\ncompositional (two units defined in different crates\nmay interact without trouble) and type-safe (the compiler\nwill inform you if you attempt to mix several incompatible\nunits of measure without converting them first). However,\nbefore using this crate, please read the rest of these\nexplanations.\n\n# Example\n\nThe following computes a speed in f64 m * s^-1\n\n```rust\nextern crate yaiouom;\n\nuse yaiouom::*;\nuse yaiouom::si::*;\n\n// The following builds unsafely with Rust, then yaiouom-checker ensures the safety of `unify`.\nfn get_speed(distance: Measure\u003cf64, Meter\u003e, duration: Measure\u003cf64, Second\u003e) -\u003e Measure\u003cf64, Mul\u003cMeter, Inv\u003cSecond\u003e\u003e\u003e {\n    return (distance / duration).unify();\n}\n\nfn main() {\n    let distance = Meter::new(100.);\n    let duration = Second::new(25.);\n    let speed = get_speed(distance, duration);\n}\n```\n\nIf you're curious about the comment and this call to `unify`, you should really\nlook at the documentation of [`unify`](https://yoric.github.io/yaiouom/yaiouom/struct.Measure.html#method.unify) :)\n\nNote that multiplication is commutative, so this is equivalent to\nthe following (we change the result of the function).\n\n\n```rust\nextern crate yaiouom;\n\nuse yaiouom::*;\nuse yaiouom::si::*;\n\n// The following builds unsafely with Rust, then yaiouom-checker ensures the safety of `unify`.\nfn get_speed(distance: Measure\u003cf64, Meter\u003e, duration: Measure\u003cf64, Second\u003e) -\u003e Measure\u003cf64, Mul\u003cInv\u003cSecond\u003e, Meter\u003e\u003e {\n    return (distance / duration).unify();\n}\n```\n\nor even to the following (we have changed the type of `distance`)\n\n```rust\nextern crate yaiouom;\n\nuse yaiouom::*;\nuse yaiouom::si::*;\n\n// The following builds unsafely with Rust, then yaiouom-checker ensures the safety of `unify`.\nfn get_speed(distance: Measure\u003cf64, Mul\u003cSecond, Mul\u003cMeter, Inv\u003cSecond\u003e\u003e\u003e, duration: Measure\u003cf64, Second\u003e) -\u003e Measure\u003cf64, Mul\u003cInv\u003cSecond\u003e, Meter\u003e\u003e {\n    return (distance / duration).unify();\n}\n```\n\nOr, if you wish to be more generic,\n\n```rust\nextern crate yaiouom;\n\nuse yaiouom::*;\nuse yaiouom::si::*;\n\n\ntrait Distance: Unit {}\ntrait Duration: Unit {}\n\n// The following builds unsafely with Rust, then yaiouom-checker ensures the safety of `unify`.\nfn get_speed_generic\u003cA: Distance, B: Duration\u003e(distance: Measure\u003cf64, A\u003e, duration: Measure\u003cf64, B\u003e) -\u003e Measure\u003cf64, Mul\u003cA, Inv\u003cB\u003e\u003e\u003e {\n    return (distance / duration).unify();\n}\n```\n\nOr, if you wish to be even more generic,\n\n```rust\nextern crate yaiouom;\n\nuse std;\n\nuse yaiouom::*;\nuse yaiouom::si::*;\n\n\ntrait Distance: Unit {}\ntrait Duration: Unit {}\n\n// The following builds unsafely with Rust, then yaiouom-checker ensures the safety of `unify`.\nfn get_speed_generic\u003cA, B, T\u003e(distance: Measure\u003cT, A\u003e, duration: Measure\u003cT, B\u003e) -\u003e Measure\u003cT::Output, Mul\u003cA, Inv\u003cB\u003e\u003e\u003e\n    where A: Distance,\n          B: Duration,\n          T: std::ops::Mul\u003cT\u003e\n{\n    return (distance / duration).unify();\n}\n```\n\nYou can easily add new units of measure:\n\n```rust\nstruct Kilometer;\nimpl BaseUnit for Kilometer {\n    const NAME: \u0026'static str = \"km\";\n}\n\nfn get_speed_km(distance: Measure\u003cf64, Kilometer\u003e, duration: Measure\u003cf64, Second\u003e) -\u003e Measure\u003cf64, Mul\u003cKilometer, Inv\u003cSecond\u003e\u003e\u003e {\n    return (distance / duration).unify();\n}\n```\n\nOn the other hand, if you attempt to write a program that misuses units of measure,\nthe companion **linter** will inform you of your error:\n\n\n```rust\nstruct Kilometer;\nimpl BaseUnit for Kilometer {\n    const NAME: \u0026'static str = \"km\";\n}\n\nfn get_speed_bad_unify(distance: Measure\u003cf64, Kilometer\u003e, duration: Measure\u003cf64, Second\u003e) -\u003e Measure\u003cf64, Mul\u003cMeter, Inv\u003cSecond\u003e\u003e\u003e {\n    return (distance / duration).unify();\n}\n\n// 69 | / fn get_speed_bad(distance: Measure\u003cf64, Kilometer\u003e, duration: Measure\u003cf64, Second\u003e) -\u003e Measure\u003cf64, Mul\u003cMeter, Inv\u003cSecond\u003e\u003e\u003e {\n// 70 | |     return ((Dimensionless::new(1.) / duration) * distance ).unify();\n//    | |            --------------------------------------------------------- in this unification\n// 71 | | }\n//    | |_^ While examining this function\n//    |\n//    = note: expected unit of measure: `Kilometer`\n//               found unit of measure: `yaiouom::si::Meter`\n```\n\nOr, if for some reason you decide to run the code without the linter,\n\n\n```\nthread 'main' panicked at 'assertion failed: `(left == right)`\n  left: `km * s^-1`,\n right: `m * s^-1`', src/unit.rs:158:9\n```\n\n# Unification and the companion linter\n\nAt the time of this writing, the Rust type system is not\npowerful enough to permit an extensible, compositional,\ntype-safe representation of units of measure. For this\nreason, other crates implementing units of measure have\nneeded to make a choice:\n\n- either prevent compositional extensibility;\n- or give up on type safety.\n\nThis crate uses a different approach, by delegating safety\nchecks to a specialized checker, `yaiouom-checker`. This\nchecker extends Rust's type system with a mechanism ensuring\nthat units of measure are used safely.\n\nIf you do not use the checker, you'll end up with a binary\nthat performs (slow) dynamic unit checking in debug builds,\nand no unit checking in optimized builds.\n\nThe linter guarantees that you'll never hit such dynamic\npanics.\n\n\u003e You really should use the companion linter :) Also, please see\n\u003e the documentation of [`unify`](https://yoric.github.io/yaiouom/yaiouom/struct.Measure.html#method.unify).\n\n\n\n# Representation of values\n\nDifferent values have different rules. Many are f32 or f64,\nbut currency computations, for instance, need to be performed\nwith either rationals or fixed point arithmetics. Some electrical\nmeasures are actually complex values. Statistics may use integer\nvalues for population, etc.\n\nFor this reason, yaiouom does not hardcode a specific representation\nof values. A value with a unit is a `Measure\u003cT, U: Unit\u003e`, where\n`T` can be any kind of number or number-like value.\n\n\n\n# Limitations\n\nAs discussed above,\n\n\u003e Please use the companion linter! Also, please see the documentation of\n\u003e [`unify`](https://yoric.github.io/yaiouom/yaiouom/struct.Measure.html#method.unify).\n\nThis crate attempts to be strictly minimal.\n\nUnit conversion is a complicated thing. We do not attempt to\nsolve this problem.\n\nSome values cannot be multiplied or divided (e.g. ºC, ºF, pH,\ndB). We do not attempt to differentiate between units that can be\nmultiplied/dived and units that can, although this might happen\nin a future version.\n\nSome values have different definitions when subtracted. For instance,\nthe difference between two dates in seconds is a duration in seconds.\nThe difference between two ºC temperatures is a value that may be\nmultiplied or divided. We do not attempt to differentiate between\nthese things.\n\n# Credits\n\nWhile this refinement type is much simpler (and more limited)\nthan the original, it draws heavy inspiration from F#'s type\nsystem.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyoric%2Fyaiouom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyoric%2Fyaiouom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyoric%2Fyaiouom/lists"}