NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
crates.io · #4391 most downloaded on crates.io
The newtype with guarantees.
Last release 17 days ago
20 Sep 2026
Release timing varies
gaps range from 8 days to 9 months
Nearly every release is documented
notes for 15 of 16 stable releases
Nothing withdrawn
no release was ever pulled
4 years old
24 releases · first in 2022
One column per quarter.
Nutype is a Rust proc macro for creating newtypes with guarantees.
Nutype is a Rust proc macro for creating newtypes with guarantees.
It adds sanitization and validation to the regular newtype pattern and makes sure those invariants hold whenever a value is constructed, including through Serde deserialization.
The main theme of this release is better interoperability with the rest of the Rust ecosystem.
Attributes on the newtype and its inner field can now be passed through to third-party derives, Serde customization is supported directly, and rust_decimal::Decimal can now be used as a first-class numeric inner type.
Previously, using third-party derives through derive_unchecked was possible, but attributes required by those derives were often lost.
In 0.8.0, attributes on the struct and inner field are forwarded to the generated type.
For example, Nutype can now play nicely with garde:
#[nutype(
sanitize(trim),
derive(Debug, Clone),
derive_unchecked(garde::Validate),
)]
pub struct UserId(
#[garde(length(min = 1))]
String
);Or with SQLx:
#[nutype(
derive(Debug),
derive_unchecked(sqlx::Type),
)]
#[sqlx(transparent)]
pub struct UserId(i64);Attributes such as #[repr(transparent)] work as well.
Since Nutype cannot know what arbitrary attributes or third-party derives do, the same caveat as with derive_unchecked applies: it is your responsibility to ensure they do not provide a way to violate the newtype's invariants.
See #229.
Nutype generates its own Serialize and Deserialize implementations so that validation cannot accidentally be bypassed.
0.8.0 now supports the commonly used Serde customization attributes:
#[serde(with = "...")]#[serde(serialize_with = "...")]#[serde(deserialize_with = "...")]#[serde(transparent)]For example:
#[nutype(
validate(predicate = |v| !v.is_empty()),
derive(Debug, Serialize, Deserialize),
)]
pub struct InvitationToken(
#[serde(with = "base64_codec")]
Vec<u8>
);Importantly, sanitization and validation still run during deserialization, including when a custom deserialize_with function is used.
See #201.
rust_decimal::Decimal supportNutype now supports rust_decimal::Decimal as a first-class numeric inner type behind the rust_decimal feature.
use nutype::nutype;
use rust_decimal::Decimal;
#[nutype(
validate(
greater_or_equal = 0,
less_or_equal = 100,
),
derive(Debug, Clone, Copy, PartialEq, PartialOrd, Display),
)]
pub struct Percentage(Decimal);The usual numeric validators and sanitizers work with Decimal, including greater, greater_or_equal, less, less_or_equal, custom predicates and custom validation functions.
Enable it with:
nutype = { version = "0.8", features = ["rust_decimal"] }
rust_decimal = "..."See #242.
0.8.0 also brings a number of smaller improvements:
validte will suggest validate.new, try_new, and new_unchecked constructors are now marked #[inline].Display messages for the less and less_or_equal float validation errors have been fixed.There was also a substantial internal cleanup: integer, float and Decimal support now share the same numeric code-generation and validation infrastructure.
#[repr(transparent)], #[sqlx(transparent)] (with derive_unchecked(sqlx::Type)), #[garde(length(min = 1))] (with derive_unchecked(garde::Validate)). Resolves #228 and the attribute half of #191.Serialize/Deserialize impls now honor field-level #[serde(with = "...")], #[serde(serialize_with = "...")], #[serde(deserialize_with = "...")] and struct-level #[serde(transparent)]. Sanitization and validation still always run on deserialization.#[cfg] on the inner field is rejected explicitly).rust_decimal::Decimal as an inner type behind the rust_decimal feature flag, with the standard numeric validators and sanitizers (see #242).#[nutype(...)] attribute is mistyped: suggests the closest match (e.g. validte -> validate) and lists the available nutype attributes (see #240).#[nutype(...)] arguments fail to parse (e.g. while still being typed), emit a best-effort type skeleton alongside the error so the newtype stays resolvable and downstream completions keep working (see #178).Display message for float newtypes: less now reads "The value must be less than ..." and less_or_equal reads "The value must be less or equal to ..." (the two were previously swapped). Integer and decimal were already correct.HashSet, whose iteration order varies between expansions, so the order of derives and generated impls in the emitted code was nondeterministic. Traits are now kept in a BTreeSet and emitted in a stable order.common/generate/numeric.rs and shared helpers in common/validate.rs), removing a large amount of duplicated code. No change to generated code or public API.new, try_new and new_unchecked constructors with #[inline] so they can be inlined across crate boundaries, matching the existing into_inner (see #237).Nothing published for this version
Nothing published for this version
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,
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, and arbitrary fuzzing.
derive_unsafe to derive_unchecked (both the feature flag and the attribute).cfg_attr for conditional derives (see #188).where clauses in generic newtypes, including Higher-Ranked Trait Bounds (HRTB) (see #160).constructor(visibility = ...) attribute (see #211).len_utf16_min and len_utf16_max validators for string types (see #162).Valuable (requires the valuable feature).cfg_attrA long-standing request (#188): derive traits only when a feature is enabled, only in tests, or under any other cfg predicate.
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 Serialize and Deserialize are only derived when the serde feature is active. Note: nutype/serde must still be enabled at compile time so the macro recognizes those traits, but the actual derive is gated by cfg_attr.
You can use any predicate the standard cfg_attr accepts, including all(...), any(...), and not(...), and you can stack multiple cfg_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.
where clauses and HRTB for generic newtypesGeneric newtypes now accept full where clauses, 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 where clauses can also be combined:
#[nutype(derive(Debug, Clone))]
struct Combined<T: Clone>(T)
where
T: Default;Until now, ::new() / ::try_new() were always pub. In v0.7.0 you can pin the constructor to any visibility you like via constructor(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), and private. With private, 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.
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_min and len_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.
ValuableWhen the valuable feature is enabled, you can now derive Valuable on your newtypes, useful for structured logging with tracing and 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.toml as:
nutype = { version = "0.7", features = ["valuable"] }
valuable = { version = "0.1", features = ["derive"] }derive_unsafe to derive_unchecked (both the feature flag and the attribute).cfg_attr for conditional derives, e.g. cfg_attr(feature = "serde", derive(Serialize, Deserialize)). Supports complex predicates and multiple entries.where clauses in generic newtypes, including Higher-Ranked Trait Bounds (HRTB) like for<'a> &'a C: IntoIterator (see #160).constructor(visibility = ...) attribute (see #211).len_utf16_min and len_utf16_max validators for string types to validate UTF-16 code unit length (useful for JavaScript interop) (see #162).Valuable (requires valuable feature).Nutype is a proc macro that adds sanitization _ and validation to newtypes, ensuring values always pass checks.
Nutype is a proc macro that adds sanitization_ and validation to newtypes, ensuring values always pass checks.
derive_unsafe(..) attribute to derive any arbitrary trait (requires derive_unsafe feature to be enabled).len_char_max and len_char_min validators. They are now clearer and easier to understand.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_unsafe feature 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_unsafe gives 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!.
derive_unsafe(..) attribute to derive any arbitrary trait (requires derive_unsafe feature to be enabled).len_char_max and len_char_min validators.Fix derive(Deserialize) for no_std (see #207 )
derive(Deserialize) for no_std (see #207)[BREAKING] The fallible ::new() constructor has been removed (it was deprecated in 0.4.3).
Nutype is a proc macro that adds sanitizatio_ and validation to newtypes, ensuring values always pass checks, even with serde deserialization.
const context if they are declared with the const_fn flag.IntoIterator for types wrapping inner types that implement IntoIterator.&'a str is now supported as an inner type.::new() constructor has been removed (it was deprecated in 0.4.3).The #[nutype] macro can now accept the const_fn flag, which instructs it to generate const fn new() / const fn try_new() functions. This allows you to create instances in a const context:
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);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);
}
}const context, when declared with const_fn flag.derive(IntoIterator) for inner types that implement IntoIterator.&'a str as an inner type.::new() constructor is removed (was deprecated in 0.4.3).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 inc
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:
no_std Support for ::core::error::Error::core::error::Error in no_std environments when using Rust version 1.81 or higher. This enhancement ensures better compatibility with modern Rust ecosystems.Custom Error Paths
You can now specify custom errors using a path, providing greater flexibility for your error-handling needs.
(#186, #187)
Deserialize Derive Compatibility
Resolved an issue where deriving Deserialize caused compilation errors when using both no_std and serde features.
(#182)
Lint Warnings
Fixed unnecessary lint warnings related to the inner generated module for cleaner builds.
Conflict with borsch Crate
Addressed a conflict with the borsch crate to ensure seamless integration.
(#195)
no_std generate implementation of ::core::error::Error if Rust version is 1.81 or higher.Deserialize derive compile when combination of no_std and serde features are used (#182)borsch crate (#195)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, t…
error and with attributes.lazy_static with std::sync::LazyLock for regex validation. This requires Rust 1.80 or higher and may cause compilation issues on older Rust versions due to the use of std::sync::LazyLock. If upgrading Rust isn't an option, you can still use lazy_static explicitly as a workaround.::new() constructor has been fully replaced by ::try_new().Previously, custom validation logic in nutype could be achieved by passing a predicate attribute, 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 with attribute for custom validation functions and the error attribute 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.
::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 a Name from 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.
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!
Nothing published for this version
Nothing published for this version
[DEPRECATION] Fallible (when a newtype has validation) constructor ::new() is deprecated. Users should use ::try_new() instead.
::new() is deprecated. Users should use ::try_new() instead.::core::result::Result when generating code for derive(TryFrom).This release comes with support of generic types for newtypes!
The example below defines SortedNotEmptyVec<T> wrapper around Vec<T> which is guaranteed to be not empty and sorted.
Note, that type bound T: Ord enables invocation of v.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]);Nothing published for this version
Support no_std ( the dependency needs to be declared as nutype = { default-features = false } )
no_std ( the dependency needs to be declared as nutype = { default-features = false } )arbitrary crate (see arbitrary feature).
Arbitrary for integer typesArbitrary for float typesArbitrary for string inner typesArbitrary for any inner typesgreater, greater_or_equal, less, less_or_equal, len_char_min, len_char_max) with expressions or named constants.#[inline] attribute to trivial functionsFailed release. Includes everything from v0.4.2 except support of Arbitrary for String based types.
Arbitrary for String based types.Nothing published for this version
Support of arbitrary inner types with custom sanitizers and validators.
greaterless#[nutype(derive(Debug))]. The regular #[derive(Debug)] syntax is not supported anymore.with has been renamed to predicate to reflect the boolean nature of its rangemin_len has been renamed to len_char_min to reflect that is based on UTF8 chars.max_len has been renamed to len_char_max to reflect that is based on UTF8 chars.max to less_or_equalmin to greater_or_equal<ValidationRule>Violated. This implies the following renames:
TooShort -> LenCharMinViolatedTooLong -> LenCharMaxViolatedEmpty -> NotEmptyViolatedRegexMismatch -> RegexViolatedInvalid -> PredicateViolatedTooBig -> LessOrEqualViolatedTooSmall -> GreaterOrEqualViolatedNotFinite -> FiniteViolatedDeserialize work with RON formatNothing published for this version
Nothing published for this version
* Support deriving of Deref
Deref[BREAKING] min_len and max_len validators run against number of characters in a string (val.chars().count()), not number of bytes (val.len()).
min_len and max_len validators run against number of characters in a string (val.chars().count()), not number of bytes (val.len()).finite validation for float types which checks against NaN and infinity.DefaultEq and Ord on float types (if finite validation is present)TryFrom for types without validation (in this case Error type is std::convert::Infallible)[BREAKING] Rename string validator present -> not_empty. Rename error variant Missing -> Empty.
present -> not_empty. Rename error variant Missing -> Empty.serde1 to serde.new_unchecked feature flag, that allows to bypass sanitization and validation.JsonSchema of schemars crate (requires schemars08 feature).regex (requires regex feature).* Initial release
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →