uom icon indicating copy to clipboard operation
uom copied to clipboard

Suggestion: Use one of `std::ops` to gain support for "literals"

Open shingtaklam1324 opened this issue 6 years ago • 6 comments

So it does what the title says: use one of std::ops, so that it can be used to create "literals" of that unit.

implementation should be simple, something like (I'm using metre and f64 as an example here, but it should be possible to do for all units, and Rem can be any of std::ops)

impl Rem<meter> for f64 {
    type Output = Length<meter, f64>;

    fn rem(self, _: meter) -> Self::Output {
        Length::new::<meter>(self)
    }
}

and then the user can use

let a = 5.0 % meter;

I'm not sure of the implementation of Length and other things within uom, but this should be able to provide a simple syntax sugar, which should make it easier and less verbose to use uom

shingtaklam1324 avatar May 26 '18 07:05 shingtaklam1324

I've written up a demo of what this could look like here:

https://github.com/shingtaklam1324/units-test/blob/f0c7868c875ecf0057a02dd1130a0cae2a9b0f01/src/main.rs#L20-L42

The syntax seems to be quite clean, although it may be better to choose an operator which isn't used as much, or something like && which shouldn't be implemented for the number types

shingtaklam1324 avatar May 26 '18 14:05 shingtaklam1324

Using * or / should likely be possible rather than choosing an arbitrary operator since all units are their own type. It may be as simple as adding impl blocks in quantity.rs for each storage type/unit combination. I'll take a look in more detail as I get some time. Until then PRs are welcome.

uom $ cargo run --example mks                       
   Compiling uom v0.18.0 (file:///.../uom)                           
error[E0277]: cannot multiply `length::meter` to `{float}`                         
 --> examples\mks.rs:9:18                                                          
  |                                                                                
9 |     let _x = 1.0 * meter;                                                      
  |                  ^ no implementation for `{float} * length::meter`             
  |                                                                                
  = help: the trait `std::ops::Mul<length::meter>` is not implemented for `{float}`

iliekturtles avatar May 29 '18 12:05 iliekturtles

Any news on this?

TatriX avatar Oct 11 '19 12:10 TatriX

I haven't had much time recently, but this issue is on the short list for review and likely inclusion in a v1.0.0 release.

iliekturtles avatar Oct 11 '19 12:10 iliekturtles

I was also looking for being able to use Add and Mul between primitive types and units to be able to give them a unit:

use uom::si::units::*;
let len = 1 * m; // si::Length
let freq = 1 / s; // si::Frequency
let vel = len * freq; // si::Velocity

gnzlbg avatar Oct 23 '19 10:10 gnzlbg

I feel like it's frowned upon to use operator overloads for an operation which doesn't match the symbol you normally use it for (e.g. * would be good for matrix multiplies, but % to attach a unit to a number feels odd)... What about defining extension traits which add methods to the primitive types instead?

For example,

trait LengthExt {
    fn metres(self) -> Length;
}

impl LengthExt for f64 {
    fn metres(self)  -> Length { Length::new::<meter>(self) }
}

Then you'd be able to write code like 3.0.metres() * 5.0.seconds().

You'd need to try it out in your own code to tell whether this is going to be just a cute trick or genuinely useful syntactic sugar though...

Michael-F-Bryan avatar Jun 18 '20 09:06 Michael-F-Bryan