`num-traits`: Numeric traits for generic mathematics


#1

Since the num crate has been deprecated and generic traits are the most frequently used parts, I developed a small crate num-traits to fill this gap. It is an implementation of the num-reform RFC, which only provides six primitive traits (Int, UnsignedInt, Signed, Float, Zero, One) to cover most use cases for generic mathematics. Zero and One are separated because they have been included in the std::num.

A big difference from the num crate is the implementation of an interface for casting numbers. The num crate introduces FromPrimitive, ToPrimitive, and NumCast traits, whereas we only use a CastInto trait as an equivelence to Into<Option<T>>. It will be unnecessary if these traits can be included in std::num.

The crate is experimental and it is not on crates.io. Any comments are welcome!


#2

Regarding the casting traits, there’s also my conv crate. It looks like CastInto is a combination of ApproxInto and ValueInto, without any error types to tell you why a conversion failed, or recover the original input (so it’s less than ideal for non-Copy types).

Oh, another advantage: ValueInto can statically tell when a conversion cannot possibly fail, allowing you to use a code path that cannot possibly panic, which might make for better code generation.


#3

Thanks for the link. I will check that.

The CastInto is only a simple wrapper for as associated with checking overflow of the type. If the number is beyond the range, it returns a None. For casting primitive numbers, this is enough, since overflow is the only reason to cause an error; for the extension to other types, returning a Result is indeed a better choice.


#4

Even with integer types, Result is useful as it allows you to distinguish between the source being too large or too small to fit into the destination range. This in turn allows for things like an_i16.value_as::<i8>().unwrap_or_saturate(), which will clamp the result appropriately: you can’t do that if all you have is None.

Also, you return Option in cases where it’s impossible for the conversion to fail, like u16u8. This leads to people adding .unwrap(), which is fine until they refactor something and suddenly the conversion can fail, but they’ve now masked that. That’s why conv has NoError and a trait defining .unwrap_ok() so that you can statically assert that a conversion cannot fail without needing a different trait.

Option also doesn’t allow you to distinguish between range violation and… whatever the hell you’d call trying to convert NaN into an integer.


#5

Thanks for your comments. I will consider changing it to a Result. It can provide more error information and more extensibility.