- You can implement custom validation
- You can use either tuple structs or regular structs (
struct Username(String)
orstruct Username { value: String }
) - Good IDE support (e.g. rename, go to definition)
- Preprocessors are supported
- Postprocessors are supported
- Visibility specifiers (e.g.
pub
,pub(crate)
) are supported for newtypes that are not restricted by validation
- It is possible to circumvent the validation by defining
impl From<String> for Username
. This is because an impl can constructUsername
directly without callingUsername::new
. There is no workaround for this, you just need to be careful. - It is possible to circumvent the validation by adding
#[derive(serde::Deserialize)]
. This is becauseimpl serde::Deserialize
can construct the target type directly without calling thenew
function. There's a workaround: add#[serde(try_from = "Foo")]
where Foo is the underlying type of this newtype.
- In macro invocations, the generics and trait bounds must be wrapped in square brackets (
[]
) instead of angle brackets (<>
). This is a limitation of macro_rules. It applies only to the definitions of generics and trait bounds, not to their usage. See examples:newtype!
- Good:
newtype!(pub struct ProjectDirectoryRef['a](&'a Path));
- Bad:
newtype!(pub struct ProjectDirectoryRef<'a>(&'a Path));
- Good:
validate_as_check!
- Good:
validate_as_check!(impl[V] Validate<V> for Even where [V: IsEven]);
- Bad:
validate_as_check!(impl<V> Validate<V> for Even where V: IsEven);
- Good:
- Constraint types must be empty structs (without fields)
- Constraint types must have the following derive attribute:
#[derive(Default, Eq, PartialEq, Hash, Clone, Copy, Debug)]
Default
is necessary to display the genericValidationError
(itsDisplay
impl requiresChecker: Display
)- Other traits are necessary to enable the same derives for newtypes
- Error types must derive
Error
(obviously) - Error types must take ownership of the value that triggered the error, so that the caller would have access to the value in an error handler
- Error type fields must be
pub
Min<Minimum, Inclusivity>
is used instead ofMin<Minimum, const INCLUSIVITY: bool>
because it provides more informative error messages.Min, Max, Equal
usePartialOrd, PartialEq
instead ofOrd, Eq
because more types implement the Partial traits. Also, if the developer specifiespub struct MyFloat(f32 | Min<0.5, Exclusive>)
, they don't wantNAN
to pass this validation. This is exactly whatMin
does by delegating toPartialCmp
, which returnsfalse
forNAN
.
- refinement
- You can't implement foreign traits on a
Refinement
type (because theRefinement
type comes from therefinement
crate, which is also foreign to your code) - You can't sanitize the value in the constructor
- You can't implement foreign traits on a
- nutype
- You can't return a specific error from the validation predicate
- You have to wait longer due to increased compilation speed
- You can't use autocompletion while writing out the macro call
- synonym
- You can automatically get certain derives based on the underlying type
- You can't get automatic derives for unknown types (for example, if
Address: Copy
, thenpub struct AddressNewtype(Address)
you won't getimpl Copy for AddressNewtype
, you need to explicitly add#[derive(Copy)]
)
- You can't get automatic derives for unknown types (for example, if
- You can automatically get certain derives based on the underlying type
- aliri_braid
- It only supports
String
as the base type
- It only supports
- prae
- Very similar design
- It's actually better
- semval
- validators