Nutype 0.3.0 released
Serhii Potapov June 25, 2023 #rust #macro #newtype #nutypeI have just released Nutype 0.3.0. In this blog post, I would like to highlight some of the new features.
What is Nutype?
Nutype is a Rust library powered by proc macros that takes type safety to the next level. For an introduction, please refer to the following resources:
Deriving Eq and Ord on f32 and f64 types
Rust has a well-known problem:
f32
and f64
types do not implement Ord
and
Eq
traits.
The reason for this is the presence of NaN
(not a number) value, which cannot be compared to any number.
As a result, even if you create a new float-based newtype it cannot derive Ord
or Eq
:
;
This will result in compiler errors:
error[E0277]: the trait bound `f64: Ord` is not satisfied
--> src/main.rs:2:17
|
1 | #[derive(PartialEq, Eq, PartialOrd, Ord)]
| --- in this derive macro expansion
2 | struct Distance(f64);
| ^^^ the trait `Ord` is not implemented for `f64`
error[E0277]: the trait bound `f64: Eq` is not satisfied
--> src/main.rs:2:17
|
1 | #[derive(PartialEq, Eq, PartialOrd, Ord)]
| -- in this derive macro expansion
2 | struct Distance(f64);
| ^^^ the trait `Eq` is not implemented for `f64`
This limitation can be annoying when you want to use floats in other structures that need to implement Eq
, and you know that your values are never going to be NaN
.
With Nutype, you can use finite
validation, which ensures that
is_finite predicate is satisfied.
This excludes NaN
values and enables correct implementation of Eq
and Ord
traits:
use nutype;
;
Deriving Default
In Nutype 0.3.0, it's now possible to derive Default
trait.
Therefore, the following code works:
use nutype;
;
Here is one tricky aspect: since the validation is dynamic, how can nutype
guarantee that the default value is valid at compile time?
Unfortunately it's not possible. So the following compiles:
;
However, it would panic when attempting to obtain an invalid OddNumber
by invoking OddNumber::default()
.
Additionally, nutype
generates a unit test to ensure that OddNumber::default()
returns a valid value.
Running cargo test
with an example above fails:
test __nutype_private_OddNumber__::should_have_valid_default_value ... FAILED
failures:
thread '__nutype_private_OddNumber__::should_have_valid_default_value' panicked at '
Default value for type OddNumber is invalid.
ERROR: invalid
This should be sufficient to catch potential errors at an early stage of development