nutype
The newtype with guarantees.
0.7.0
5.4M downloads/mo
#4371 most downloaded on crates.io
greyblake/nutype
What this package is like to depend on
Last release 2 months ago
18 Jun 2026
Release timing varies
gaps range from 8 days to 9 months
Nearly every release is documented
notes for 14 of 15 stable releases
Nothing withdrawn
no release was ever pulled
4 years old
22 releases · first in 2022
2 releases in the last 12 months
see the full history below
Release timeline
22 releases · Oct 2022 to Jun 2026Releases
latest 22-
0.8.0-beta.118 Jun 2026 pre-releaseNothing published for this version
-
0.7.025 Apr 2026Release notes
Open source →What is nutype?
Nutype is a Rust proc macro for type-driven domain modeling: it turns the newtype pattern into refined, branded types with built-in sanitizers (trim, lowercase, custom) and validators (length, range, regex, predicates, custom errors). It's "parse, don't validate" applied to your domain, useful for DDD, schema validation at API boundaries, and making illegal states unrepresentable, even across serde,
FromStr, andarbitraryfuzzing.Changes in v0.7.0
- [BREAKING] Renamed
derive_unsafetoderive_unchecked(both the feature flag and the attribute). - [FEATURE] Support
cfg_attrfor conditional derives (see #188). - [FEATURE] Support
whereclauses in generic newtypes, including Higher-Ranked Trait Bounds (HRTB) (see #160). - [FEATURE] Ability to control constructor visibility with the
constructor(visibility = ...)attribute (see #211). - [FEATURE] Add
len_utf16_minandlen_utf16_maxvalidators for string types (see #162). - [FEATURE] Ability to derive
Valuable(requires thevaluablefeature).
Conditional derives via
cfg_attrA long-standing request (#188): derive traits only when a feature is enabled, only in tests, or under any other
cfgpredicate.In v0.7.0 you can now write
cfg_attr(...)directly inside#[nutype(...)], mirroring the standard#[cfg_attr]syntax:use nutype::nutype; #[nutype( sanitize(trim, lowercase), validate(not_empty, len_char_max = 100), derive(Debug, Clone, PartialEq, AsRef), cfg_attr(feature = "serde", derive(Serialize, Deserialize)) )] pub struct Email(String);
Here
SerializeandDeserializeare only derived when theserdefeature is active. Note:nutype/serdemust still be enabled at compile time so the macro recognizes those traits, but the actualderiveis gated bycfg_attr.You can use any predicate the standard
cfg_attraccepts, includingall(...),any(...), andnot(...), and you can stack multiplecfg_attr(..)entries:#[nutype( validate(not_empty), derive(Debug), cfg_attr(test, derive(Clone)), cfg_attr(feature = "serde", derive(Serialize, Deserialize)) )] pub struct Tag(String);
A complete walkthrough lives in
examples/cfg_attr_example.whereclauses and HRTB for generic newtypesGeneric newtypes now accept full
whereclauses, including Higher-Ranked Trait Bounds (#160):use nutype::nutype; #[nutype( validate(predicate = |c| c.into_iter().next().is_some()), derive(Debug) )] struct NonEmpty<C>(C) where for<'a> &'a C: IntoIterator; let xs = NonEmpty::try_new(vec![1, 2, 3]).unwrap();
Inline bounds and
whereclauses can also be combined:#[nutype(derive(Debug, Clone))] struct Combined<T: Clone>(T) where T: Default;
Constructor visibility
Until now,
::new()/::try_new()were alwayspub. In v0.7.0 you can pin the constructor to any visibility you like viaconstructor(visibility = ...)(#211):use nutype::nutype; #[nutype( constructor(visibility = pub(crate)), validate(not_empty), derive(Debug, AsRef), )] pub struct InternalId(String);
Supported values are
pub,pub(crate),pub(super),pub(in path), andprivate. Withprivate, the constructor is only callable from the module where the type is defined, useful for types that should only be obtained through a higher-level factory.UTF-16 length validators
JavaScript and a few other ecosystems count string length in UTF-16 code units rather than Unicode characters or bytes. To make interop straightforward, v0.7.0 adds
len_utf16_minandlen_utf16_max(#162):use nutype::nutype; #[nutype( validate(len_utf16_min = 1, len_utf16_max = 280), derive(Debug, AsRef), )] pub struct Tweet(String);
These complement the existing
len_char_min/len_char_max(Unicode chars) validators.Deriving
ValuableWhen the
valuablefeature is enabled, you can now deriveValuableon your newtypes, useful for structured logging withtracingand similar instrumentation:use nutype::nutype; use valuable::Valuable; #[nutype(derive(Valuable))] pub struct Age(u32); #[nutype(derive(Valuable))] pub struct Name(String); assert_eq!(format!("{:?}", Age::new(25).as_value()), "Age(25)");
Add it to your
Cargo.tomlas:nutype = { version = "0.7", features = ["valuable"] } valuable = { version = "0.1", features = ["derive"] }
Links
Release notes
Open source →- [BREAKING] Rename
derive_unsafetoderive_unchecked(both the feature flag and the attribute). - [FEATURE] Support
cfg_attrfor conditional derives, e.g.cfg_attr(feature = "serde", derive(Serialize, Deserialize)). Supports complex predicates and multiple entries. - [FEATURE] Support
whereclauses in generic newtypes, including Higher-Ranked Trait Bounds (HRTB) likefor<'a> &'a C: IntoIterator(see #160). - [FEATURE] Ability to control constructor visibility with
constructor(visibility = ...)attribute (see #211). - [FEATURE] Add
len_utf16_minandlen_utf16_maxvalidators for string types to validate UTF-16 code unit length (useful for JavaScript interop) (see #162). - [FEATURE] Ability to derive
Valuable(requiresvaluablefeature).
- [BREAKING] Renamed
-
0.6.230 Jul 2025Release notes
Open source →What is nutype?
Nutype is a proc macro that adds sanitization_ and validation to newtypes, ensuring values always pass checks.
Changes in v0.6.2
- [FEATURE] Introduce
derive_unsafe(..)attribute to derive any arbitrary trait (requiresderive_unsafefeature to be enabled).
- [FIX] Updated the Rust edition: 2021 → 2024.
- [FIX] Improved error messages for
len_char_maxandlen_char_minvalidators. They are now clearer and easier to understand.
derive_unsafe
You can now use the new
derive_unsafe(..)attribute to derive arbitrary traits, including third-party ones, which are not known to nutype.Unlike
derive(..), this mechanism bypasses nutype’s internal safety checks, meaning it's possible to violate validation rules at runtime if you're deriving a trait that has methods that instantiate or mutate a value.It requires
derive_unsafefeature flag to be enabled.Example (do not copy blindly):
use derive_more::{Deref, DerefMut}; use nutype::nutype; #[nutype( derive(Debug, AsRef), derive_unsafe(Deref, DerefMut), validate(greater_or_equal = 0.0, less_or_equal = 2.0) )] struct LlmTemperature(f64); fn main() { let mut temperature = LlmTemperature::try_new(1.5).unwrap(); // This is not what nutype is designed for! *temperature = 2.5; // OH no, we've just violated the validation rule! assert_eq!(temperature.as_ref(), &2.5); }
The takeaway:
derive_unsafegives you raw power—but you’re on your own to ensure your type’s invariants aren't broken.On that note, have a nice day!.
Release notes
Open source →- [FEATURE] Introduce
derive_unsafe(..)attribute to derive any arbitrary trait (requiresderive_unsafefeature to be enabled). - [FIX] Update Rust edition: 2021 -> 2024.
- [FIX] Improve error messages for
len_char_maxandlen_char_minvalidators.
- [FEATURE] Introduce
-
0.6.109 Feb 2025 -
0.6.002 Feb 2025Release notes
Open source →What is nutype?
Nutype is a proc macro that adds sanitizatio_ and validation to newtypes, ensuring values always pass checks, even with serde deserialization.
Changes in v0.6.0
- [FEATURE] You can now instantiate types in a
constcontext if they are declared with theconst_fnflag. - [FEATURE] You can now derive
IntoIteratorfor types wrapping inner types that implementIntoIterator. - [FIX]
&'a stris now supported as an inner type. - [BREAKING] The fallible
::new()constructor has been removed (it was deprecated in 0.4.3).
Const functions
The
#[nutype]macro can now accept theconst_fnflag, which instructs it to generateconst fn new()/const fn try_new()functions. This allows you to create instances in aconstcontext:use nutype::nutype; #[nutype( const_fn, validate(greater_or_equal = -1.0, less_or_equal = 1.0) )] struct Correlation(f64); // Since Result::unwrap() is not yet supported in a const context, we need to handle the result manually: const ZERO_CORRELATION: Correlation = match Correlation::try_new(0.0) { Ok(c) => c, Err(_) => panic!("Invalid Correlation value"), };
Because manually unwrapping in a const context can be tedious, you can use a helper macro like the one below (not part of nutype):
macro_rules! nutype_const { ($name:ident, $ty:ty, $value:expr) => { const $name: $ty = match <$ty>::try_new($value) { Ok(value) => value, Err(_) => panic!("Invalid value"), }; }; } nutype_const!(ZERO_CORRELATION, Correlation, 0.0);
IntoIterator
Types that wrap a collection can now derive IntoIterator. This automatically provides both a consuming iterator (
impl IntoIterator for T) and an iterator over references (impl IntoIterator for &T).Example:
use nutype::nutype; #[nutype(derive(IntoIterator))] struct Names(Vec<String>); fn main() { let names = Names::new(vec![ "Alice".to_string(), "Bob".to_string(), ]); // Iterate over references for name in &names { println!("{}", name); } // Iterate over owned values (consuming iterator) for name in names { println!("{}", name); } }
Release notes
Open source →- [FEATURE] Ability to instantiate types in
constcontext, when declared withconst_fnflag. - [FEATURE] Support
derive(IntoIterator)for inner types that implementIntoIterator. - [FIX] Enable
&'a stras an inner type. - [BREAKING] Fallible
::new()constructor is removed (was deprecated in 0.4.3).
- [FEATURE] You can now instantiate types in a
-
0.5.120 Dec 2024Release notes
Open source →I am excited to announce the release of Nutype 0.5.1, which brings some fixes for an improved developer experience. Below is an overview of what's included in this version:
🚀 New Features
no_stdSupport for::core::error::Error
Nutype now generates an implementation of::core::error::Errorinno_stdenvironments when using Rust version 1.81 or higher. This enhancement ensures better compatibility with modern Rust ecosystems.
🛠️ Bug Fixes
-
Custom Error Paths
You can now specify custom errors using a path, providing greater flexibility for your error-handling needs.
(#186, #187) -
DeserializeDerive Compatibility
Resolved an issue where derivingDeserializecaused compilation errors when using bothno_stdandserdefeatures.
(#182) -
Lint Warnings
Fixed unnecessary lint warnings related to the inner generated module for cleaner builds. -
Conflict with
borschCrate
Addressed a conflict with theborschcrate to ensure seamless integration.
(#195)
Release notes
Open source →- [FEATURE] In
no_stdgenerate implementation of::core::error::Errorif Rust version is 1.81 or higher. - [FIX] Enable to specify custom error as a path (see #186, #187)
- [FIX] Make
Deserializederive compile when combination ofno_stdandserdefeatures are used (#182) - [FIX] Fix lint warnings about inner generated module
- [FIX] Fix a conflict when used with
borschcrate (#195)
-
0.5.002 Sep 2024Release notes
Open source →Changes
- [FEATURE] Added support for custom error types and validation functions via the
errorandwithattributes. - [BREAKING] Replaced
lazy_staticwithstd::sync::LazyLockfor regex validation. This requires Rust 1.80 or higher and may cause compilation issues on older Rust versions due to the use ofstd::sync::LazyLock. If upgrading Rust isn't an option, you can still uselazy_staticexplicitly as a workaround. - [BREAKING] The fallible
::new()constructor has been fully replaced by::try_new().
Highlights
Custom errors
Previously, custom validation logic in nutype could be achieved by passing a
predicateattribute, as shown below:#[nutype(validate(predicate = |n| n % 2 == 1))] struct OddNumber(i64);
This would automatically generate a simple error type:
enum OddNumberError { PredicateViolated, }
However, this approach often lacked flexibility. Many users needed more detailed error handling. For example, some users wanted to attach additional information to errors or provide more descriptive error messages. Others preferred to use a single error type across their application, but found it cumbersome to map very specific errors to a more general error type.
To address these needs, nutype now introduces the
withattribute for custom validation functions and theerrorattribute for specifying custom error types:use nutype::nutype; // Define a newtype `Name` with custom validation logic and a custom error type `NameError`. // If validation fails, `Name` cannot be instantiated. #[nutype( validate(with = validate_name, error = NameError), derive(Debug, AsRef, PartialEq), )] struct Name(String); // Custom error type for `Name` validation. // You can use `thiserror` or similar crates to provide more detailed error messages. #[derive(Debug, PartialEq)] enum NameError { TooShort { min: usize, length: usize }, TooLong { max: usize, length: usize }, } // Validation function for `Name` that checks its length. fn validate_name(name: &str) -> Result<(), NameError> { const MIN: usize = 3; const MAX: usize = 10; let length = name.encode_utf16().count(); if length < MIN { Err(NameError::TooShort { min: MIN, length }) } else if length > MAX { Err(NameError::TooLong { max: MAX, length }) } else { Ok(()) } }
With this enhancement, users have full control over the error type and how errors are constructed during validation, making the error handling process more powerful and adaptable to different use cases.
Transition from fallible
::new()to::try_new()In version 0.4.3, the fallible
::new()constructor was deprecated but still available. Now, it has been fully replaced by::try_new(). For example, to initialize aNamefrom the previous example:let name = Name::try_new("Anton").unwrap();
This change ensures a more consistent and explicit error-handling approach when creating instances.
Note that::new()is still used as a non-fallible constructor if there a newtype has no validation.The sponsors ❤️
A big shoutout to the true sponsors of this release - my in-laws! Thanks for taking care of my wife and kid, giving me a free weekend to work on Nutype!
Links
Release notes
Open source →- [FEATURE] Added support for custom error types and validation functions via the
errorandwithattributes. - [BREAKING] Replaced
lazy_staticwithstd::sync::LazyLockfor regex validation. This requires Rust 1.80 or higher and may cause compilation issues on older Rust versions due to the use ofstd::sync::LazyLock. If upgrading Rust isn't an option, you can still uselazy_staticexplicitly as a workaround. - [BREAKING] The fallible
::new()constructor has been fully replaced by::try_new().
- [FEATURE] Added support for custom error types and validation functions via the
-
0.5.0-beta.225 Aug 2024 pre-releaseNothing published for this version
-
0.5.0-beta.124 Aug 2024 pre-releaseNothing published for this version
-
0.4.306 Jul 2024Release notes
Open source →Changes
- Support generics
- [DEPRECATION] Fallible (when a newtype has validation) constructor
::new()is deprecated. Users should use::try_new()instead. - [FIX] Use absolute path for
::core::result::Resultwhen generating code forderive(TryFrom).
Highlights
This release comes with support of generic types for newtypes!
The example below definesSortedNotEmptyVec<T>wrapper aroundVec<T>which is guaranteed to be not empty and sorted.
Note, that type boundT: Ordenables invocation ofv.sort()in the sanitization function.use nutype::nutype; #[nutype( sanitize(with = |mut v| { v.sort(); v }), validate(predicate = |vec| !vec.is_empty()), derive(Debug, PartialEq, AsRef), )] struct SortedNotEmptyVec<T: Ord>(Vec<T>); let wise_friends = SortedNotEmptyVec::try_new(vec!["Seneca", "Zeno", "Plato"]).unwrap(); assert_eq!(wise_friends.as_ref(), &["Plato", "Seneca", "Zeno"]); let numbers = SortedNotEmptyVec::try_new(vec![4, 2, 7, 1]).unwrap(); assert_eq!(numbers.as_ref(), &[1, 2, 4, 7]);
Links
Release notes
Open source →- Support generics
- [DEPRECATION] Fallible constructor
::new()is deprecated. Users should use::try_new()instead. - [FIX] Use absolute path for
::core::result::Resultwhen generating code forderive(TryFrom).
-
0.4.3-beta.101 Jul 2024 pre-releaseNothing published for this version
-
0.4.207 Apr 2024Release notes
Open source →- Support
no_std( the dependency needs to be declared asnutype = { default-features = false }) - Support integration with
arbitrarycrate (seearbitraryfeature).- Support
Arbitraryfor integer types - Support
Arbitraryfor float types - Support
Arbitraryfor string inner types - Support
Arbitraryfor any inner types
- Support
- Possibility to specify boundaries (
greater,greater_or_equal,less,less_or_equal,len_char_min,len_char_max) with expressions or named constants. - Add
#[inline]attribute to trivial functions - Improve error messages
- Support
-
0.4.107 Apr 2024Release notes
Open source →- Failed release. Includes everything from v0.4.2 except support of
ArbitraryforStringbased types.
- Failed release. Includes everything from v0.4.2 except support of
-
0.4.1-beta.128 Jan 2024 pre-releaseNothing published for this version
-
0.4.020 Nov 2023Release notes
Open source →- Support of arbitrary inner types with custom sanitizers and validators.
- Add numeric validator
greater - Add numeric validator
less - [BREAKING] Removal of asterisk derive
- [BREAKING] Use commas to separate high level attributes
- [BREAKING] Traits are derived with
#[nutype(derive(Debug))]. The regular#[derive(Debug)]syntax is not supported anymore. - [BREAKING] Validator
withhas been renamed topredicateto reflect the boolean nature of its range - [BREAKING] String validator
min_lenhas been renamed tolen_char_minto reflect that is based on UTF8 chars. - [BREAKING] String validator
max_lenhas been renamed tolen_char_maxto reflect that is based on UTF8 chars. - [BREAKING] Rename numeric validator
maxtoless_or_equal - [BREAKING] Rename numeric validator
mintogreater_or_equal - [BREAKING] Rename error variants to follow the following formula:
<ValidationRule>Violated. This implies the following renames:TooShort->LenCharMinViolatedTooLong->LenCharMaxViolatedEmpty->NotEmptyViolatedRegexMismatch->RegexViolatedInvalid->PredicateViolatedTooBig->LessOrEqualViolatedTooSmall->GreaterOrEqualViolatedNotFinite->FiniteViolated
- Better error messages: in case of unknown attribute, validator or sanitizer the possible values are listed.
- [FIX] Make derived
Deserializework with RON format
-
0.4.0-beta.228 Aug 2023 pre-releaseNothing published for this version
-
0.4.0-beta.127 Aug 2023 pre-releaseNothing published for this version
-
0.3.130 Jun 2023 -
0.3.025 Jun 2023Release notes
Open source →- [BREAKING]
min_lenandmax_lenvalidators run against number of characters in a string (val.chars().count()), not number of bytes (val.len()). - Add
finitevalidation for float types which checks against NaN and infinity. - Support deriving of
Default - Support deriving of
EqandOrdon float types (iffinitevalidation is present) - Support deriving of
TryFromfor types without validation (in this case Error type isstd::convert::Infallible)
- [BREAKING]
-
0.2.013 Apr 2023Release notes
Open source →- [BREAKING] Rename string validator
present->not_empty. Rename error variantMissing->Empty. - [BREAKING] Rename feature
serde1toserde. - Introduce
new_uncheckedfeature flag, that allows to bypass sanitization and validation. - Support derive of
JsonSchemaofschemarscrate (requiresschemars08feature). - Support string validation with
regex(requiresregexfeature).
- [BREAKING] Rename string validator
-
0.1.111 Feb 2023 -
0.1.003 Oct 2022Nothing published for this version