{"id":13484346,"url":"https://github.com/greyblake/nutype","last_synced_at":"2025-04-23T20:54:16.037Z","repository":{"id":65811972,"uuid":"548568646","full_name":"greyblake/nutype","owner":"greyblake","description":"Rust newtype with guarantees  🇺🇦 🦀","archived":false,"fork":false,"pushed_at":"2025-03-06T17:44:11.000Z","size":1197,"stargazers_count":1537,"open_issues_count":13,"forks_count":26,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-01T23:43:32.741Z","etag":null,"topics":["data","data-structures","invariance","invariant","invariants","macro","macros","newtype","rust","rust-lang","rust-library","sanitization","sanitizer","typesafety","validation","validator","web"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/greyblake.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-MIT","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-09T20:44:18.000Z","updated_at":"2025-03-25T18:17:47.000Z","dependencies_parsed_at":"2023-11-18T13:30:52.601Z","dependency_job_id":"7afe27a4-5561-4d4f-8302-96a5aa6b931f","html_url":"https://github.com/greyblake/nutype","commit_stats":{"total_commits":194,"total_committers":3,"mean_commits":64.66666666666667,"dds":"0.030927835051546393","last_synced_commit":"cd8f0b5649122c65144239f7e783df681c72b60d"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greyblake%2Fnutype","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greyblake%2Fnutype/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greyblake%2Fnutype/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greyblake%2Fnutype/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/greyblake","download_url":"https://codeload.github.com/greyblake/nutype/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247953032,"owners_count":21023947,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data","data-structures","invariance","invariant","invariants","macro","macros","newtype","rust","rust-lang","rust-library","sanitization","sanitizer","typesafety","validation","validator","web"],"created_at":"2024-07-31T17:01:22.845Z","updated_at":"2025-04-09T01:03:00.397Z","avatar_url":"https://github.com/greyblake.png","language":"Rust","readme":"\u003cp align=\"center\"\u003e\n\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/greyblake/nutype/master/art/rust_nutype_inverted.png\"\u003e\n  \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/greyblake/nutype/master/art/rust_nutype.png\"\u003e\n\n  \u003cimg width=\"300\" alt=\"Rust Nutype Logo\" src=\"https://raw.githubusercontent.com/greyblake/nutype/master/art/rust_nutype.png\"\u003e\n\u003c/picture\u003e\n\u003c/p\u003e\n\u003ch2 align=\"center\"\u003eThe newtype with guarantees.\u003c/h2\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/greyblake/nutype/actions/workflows/ci.yml\" rel=\"nofollow\"\u003e\u003cimg src=\"https://github.com/greyblake/nutype/actions/workflows/ci.yml/badge.svg\" alt=\"Nutype Build Status\"\u003e\u003c/a\u003e\n\u003ca href=\"https://docs.rs/nutype\" rel=\"nofollow\"\u003e\u003cimg src=\"https://docs.rs/nutype/badge.svg\" alt=\"Nutype Documentation\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/greyblake/nutype/discussions\"\u003e\u003cimg src=\"https://img.shields.io/github/discussions/greyblake/nutype\"/\u003e\u003c/a\u003e\n\u003cp\u003e\n\n\nNutype is a proc macro that allows adding extra constraints like _sanitization_ and _validation_ to the regular [newtype pattern](https://doc.rust-lang.org/rust-by-example/generics/new_types.html). The generated code makes it impossible to instantiate a value without passing the checks. It works this way even with `serde` deserialization.\n\n\n* [Quick start](#quick-start)\n* [Inner types](#inner-types) ([String](#string) | [Integer](#integer) | [Float](#float) | [Other](#other-inner-types-and-generics))\n* [Custom](#custom-sanitizers) ([sanitizers](#custom-sanitizers) | [validators](#custom-validators) | [errors](#custom-validation-with-a-custom-error-type))\n* [Constants](#constants)\n* [Recipes](#recipes)\n* [Breaking constraints with new_unchecked](#breaking-constraints-with-new_unchecked)\n* [Feature Flags](#feature-flags)\n* [Support Ukrainian military forces](#support-ukrainian-military-forces)\n* [Similar projects](#similar-projects)\n\n## Quick start\n\n```rust\nuse nutype::nutype;\n\n// Define newtype Username\n#[nutype(\n    sanitize(trim, lowercase),\n    validate(not_empty, len_char_max = 20),\n    derive(Debug, PartialEq, Clone),\n)]\npub struct Username(String);\n\n// We can obtain a value of Username with `::try_new()`.\n// Note that Username holds a sanitized string\nassert_eq!(\n    Username::try_new(\"   FooBar  \").unwrap().into_inner(),\n    \"foobar\"\n);\n\n// It's impossible to obtain an invalid Username\n// Note that we also got `UsernameError` enum generated implicitly\n// based on the validation rules.\nassert_eq!(\n    Username::try_new(\"   \"),\n    Err(UsernameError::NotEmptyViolated),\n);\nassert_eq!(\n    Username::try_new(\"TheUserNameIsVeryVeryLong\"),\n    Err(UsernameError::LenCharMaxViolated),\n);\n```\n\nFor more please see:\n* [Examples](https://github.com/greyblake/nutype/tree/master/examples)\n* [Tests](https://github.com/greyblake/nutype/tree/master/test_suite/tests)\n\n\n## Inner types\n\nAvailable sanitizers, validators, and derivable traits are determined by the inner type, which falls into the following categories:\n* String\n* Integer (`u8`, `u16`,`u32`, `u64`, `u128`, `i8`, `i16`, `i32`, `i64`, `i128`, `usize`, `isize`)\n* Float (`f32`, `f64`)\n* Anything else\n\n## String\n\nAt the moment the string inner type supports only `String` (owned) type.\n\n### String sanitizers\n\n| Sanitizer   | Description                                                                         | Example                                         |\n|-------------|-------------------------------------------------------------------------------------|-------------------------------------------------|\n| `trim`      | Removes leading and trailing whitespaces                                            | `trim`                                          |\n| `lowercase` | Converts the string to lowercase                                                    | `lowercase`                                     |\n| `uppercase` | Converts the string to uppercase                                                    | `uppercase`                                     |\n| `with`      | Custom sanitizer. A function or closure that receives `String` and returns `String` | `with = \\|mut s: String\\| ( s.truncate(5); s )` |\n\n### String validators\n\n| Validator      | Description                                                                     | Error variant        | Example                                      |\n|----------------|---------------------------------------------------------------------------------|----------------------|----------------------------------------------|\n| `len_char_min` | Min length of the string (in chars, not bytes)                                  | `LenCharMinViolated` | `len_char_min = 5`                           |\n| `len_char_max` | Max length of the string (in chars, not bytes)                                  | `LenCharMaxViolated` | `len_char_max = 255`                         |\n| `not_empty`    | Rejects an empty string                                                         | `NotEmptyViolated`   | `not_empty`                                  |\n| `regex`        | Validates format with a regex. Requires `regex` feature.                        | `RegexViolated`      | `regex = \"^[0-9]{7}$\"` or `regex = ID_REGEX` |\n| `predicate`    | Custom validator. A function or closure that receives `\u0026str` and returns `bool` | `PredicateViolated`  | `predicate = \\|s: \u0026str\\| s.contains('@')`    |\n| `with`         | Custom validator with a custom error                                            | N/A                  | (see example below)                          |\n\n\n#### Regex validation\n\nRequirements:\n* `regex` feature of `nutype` is enabled.\n* You have to explicitly include `regex` as a dependency.\n\nThere are a number of ways you can use regex.\n\nA regular expression can be defined right in place:\n\n```rs\n#[nutype(validate(regex = \"^[0-9]{3}-[0-9]{3}$\"))]\npub struct PhoneNumber(String);\n```\n\nor it can be defined with `std::sync::LazyLock`:\n\n```rs\nuse regex::Regex;\n\nstatic PHONE_NUMBER_REGEX: LazyLock\u003cRegex\u003e = LazyLock::new(|| Regex::new(\"^[0-9]{3}-[0-9]{3}$\").unwrap());\n\n#[nutype(validate(regex = PHONE_NUMBER_REGEX))]\npub struct PhoneNumber(String);\n```\n\nor it can be defined with `lazy_static`:\n\n```rs\nuse lazy_static::lazy_static;\nuse regex::Regex;\n\nlazy_static! {\n    static ref PHONE_NUMBER_REGEX: Regex = Regex::new(\"^[0-9]{3}-[0-9]{3}$\").unwrap();\n}\n\n#[nutype(validate(regex = PHONE_NUMBER_REGEX))]\npub struct PhoneNumber(String);\n```\n\nor `once_cell`:\n\n```rs\nuse once_cell::sync::Lazy;\nuse regex::Regex;\n\nstatic PHONE_NUMBER_REGEX: Lazy\u003cRegex\u003e =\n    Lazy::new(|| Regex::new(\"[0-9]{3}-[0-9]{3}$\").unwrap());\n\n#[nutype(validate(regex = PHONE_NUMBER_REGEX))]\npub struct PhoneNumber(String);\n```\n\n\n### String derivable traits\n\nThe following traits can be derived for a string-based type:\n`Debug`, `Clone`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `FromStr`, `AsRef`, `Deref`,\n`From`, `TryFrom`, `Into`, `Hash`, `Borrow`, `Display`, `Default`, `Serialize`, `Deserialize`.\n\n\n## Integer\n\nThe integer inner types are: `u8`, `u16`,`u32`, `u64`, `u128`, `i8`, `i16`, `i32`, `i64`, `i128`, `usize`, `isize`.\n\n### Integer sanitizers\n\n| Sanitizer | Description       | Example                            |\n|-----------|-------------------|------------------------------------|\n| `with`    | Custom sanitizer. | `with = \\|raw\\| raw.clamp(0, 100)` |\n\n### Integer validators\n\n| Validator           | Description                           | Error variant             | Example                              |\n| ------------------- | ------------------------------------- | ------------------------- | ------------------------------------ |\n| `less`              | Exclusive upper bound                 | `LessViolated`            | `less = 100`                         |\n| `less_or_equal`     | Inclusive upper bound                 | `LessOrEqualViolated`     | `less_or_equal = 99`                 |\n| `greater`           | Exclusive lower bound                 | `GreaterViolated`         | `greater = 17`                       |\n| `greater_or_equal`  | Inclusive lower bound                 | `GreaterOrEqualViolated`  | `greater_or_equal = 18`              |\n| `predicate`         | Custom predicate                      | `PredicateViolated`       | `predicate = \\|num\\| num % 2 == 0`   |\n| `with`              | Custom validator with a custom error  | N/A                       | (see example below)                  |\n\n### Integer derivable traits\n\nThe following traits can be derived for an integer-based type:\n`Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `FromStr`, `AsRef`, `Deref`,\n`Into`, `From`, `TryFrom`, `Hash`, `Borrow`, `Display`, `Default`, `Serialize`, `Deserialize`.\n\n\n## Float\n\nThe float inner types are: `f32`, `f64`.\n\n### Float sanitizers\n\n| Sanitizer | Description       | Example                                |\n|-----------|-------------------|----------------------------------------|\n| `with`    | Custom sanitizer. | `with = \\|val\\| val.clamp(0.0, 100.0)` |\n\n### Float validators\n\n| Validator          | Description                          | Error variant            | Example                             |\n| ------------------ | ------------------------------------ | ------------------------ | ----------------------------------- |\n| `less`             | Exclusive upper bound                | `LessViolated`           | `less = 100.0`                      |\n| `less_or_equal`    | Inclusive upper bound                | `LessOrEqualViolated`    | `less_or_equal = 100.0`             |\n| `greater`          | Exclusive lower bound                | `GreaterViolated`        | `greater = 0.0`                     |\n| `greater_or_equal` | Inclusive lower bound                | `GreaterOrEqualViolated` | `greater_or_equal = 0.0`            |\n| `finite`           | Check against NaN and infinity       | `FiniteViolated`         | `finite`                            |\n| `predicate`        | Custom predicate                     | `PredicateViolated`      | `predicate = \\|val\\| val != 50.0`   |\n| `with`             | Custom validator with a custom error | N/A                      | (see example below)                 |\n\n### Float derivable traits\n\nThe following traits can be derived for a float-based type:\n`Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `FromStr`, `AsRef`, `Deref`,\n`Into`, `From`, `TryFrom`, `Hash`, `Borrow`, `Display`, `Default`, `Serialize`, `Deserialize`.\n\nIt's also possible to derive `Eq` and `Ord` if the validation rules guarantee that `NaN` is excluded.\nThis can be done applying by `finite` validation. For example:\n\n```rust\n#[nutype(\n    validate(finite),\n    derive(PartialEq, Eq, PartialOrd, Ord),\n)]\nstruct Size(f64);\n```\n\n## Other inner types and generics\n\nFor any other type it is possible to define custom sanitizers with `with` and custom\nvalidations with `predicate`:\n\n```rust\nuse nutype::nutype;\n\n#[nutype(\n    derive(Debug, PartialEq, AsRef, Deref),\n    sanitize(with = |mut guests| { guests.sort(); guests }),\n    validate(predicate = |guests| !guests.is_empty() ),\n)]\npub struct GuestList(Vec\u003cString\u003e);\n```\n\nIt's also possible to use generics:\n\n```rust\n#[nutype(\n    sanitize(with = |mut v| { v.sort(); v }),\n    validate(predicate = |vec| !vec.is_empty()),\n    derive(Debug, PartialEq, AsRef, Deref),\n)]\nstruct SortedNotEmptyVec\u003cT: Ord\u003e(Vec\u003cT\u003e);\n\nlet wise_friends = SortedNotEmptyVec::try_new(vec![\"Seneca\", \"Zeno\", \"Plato\"]).unwrap();\nassert_eq!(wise_friends.as_ref(), \u0026[\"Plato\", \"Seneca\", \"Zeno\"]);\nassert_eq!(wise_friends.len(), 3);\n\nlet numbers = SortedNotEmptyVec::try_new(vec![4, 2, 7, 1]).unwrap();\nassert_eq!(numbers.as_ref(), \u0026[1, 2, 4, 7]);\nassert_eq!(numbers.len(), 4);\n```\n\n\n\n## Custom sanitizers\n\nYou can set custom sanitizers using the `with` option.\nA custom sanitizer is a function or closure that receives a value of an inner type with ownership and returns a sanitized value.\n\nFor example, this one\n\n```rust\n#[nutype(sanitize(with = new_to_old))]\npub struct CityName(String);\n\nfn new_to_old(s: String) -\u003e String {\n    s.replace(\"New\", \"Old\")\n}\n```\n\nis equal to the following one:\n\n```rust\n#[nutype(sanitize(with = |s| s.replace(\"New\", \"Old\") ))]\npub struct CityName(String);\n```\n\nAnd works the same way:\n\n```rust\nlet city = CityName::new(\"New York\");\nassert_eq!(city.into_inner(), \"Old York\");\n```\n\n## Custom validators\n\nIn similar fashion it's possible to define custom validators, but a validation function receives a reference and returns `bool`.\nThink of it as a predicate.\n\n```rust\n#[nutype(validate(predicate = is_valid_name))]\npub struct Name(String);\n\nfn is_valid_name(name: \u0026str) -\u003e bool {\n    // A fancy way to verify if the first character is uppercase\n    name.chars().next().map(char::is_uppercase).unwrap_or(false)\n}\n```\n\n## Custom validation with a custom error type\n\nTo define your own error type and implement custom validation logic, you can combine the `with` and `error` attributes:\n\n```rust\n// Define a custom error type for validation failures.\n// Although it's best practice to implement `std::error::Error` for custom error types,\n// we are omitting that for simplicity here.\n#[derive(Debug, PartialEq)]\nenum NameError {\n    TooShort,\n    TooLong,\n}\n\n// Define a custom validation function for `Name`.\n// The function returns `Result\u003c(), NameError\u003e`, where `Ok(())` indicates a valid name,\n// and `Err(NameError)` represents a specific validation failure.\nfn validate_name(name: \u0026str) -\u003e Result\u003c(), NameError\u003e {\n    if name.len() \u003c 3 {\n        Err(NameError::TooShort)\n    } else if name.len() \u003e 10 {\n        Err(NameError::TooLong)\n    } else {\n        Ok(())\n    }\n}\n\n// Define a newtype `Name` with custom validation logic and custom error.\n#[nutype(\n    validate(with = validate_name, error = NameError),\n    derive(Debug, PartialEq),\n)]\nstruct Name(String);\n```\n\nIt's important to ensure that the type specified in the `error` attribute matches the error type returned by the validation function.\n\n## Constants\n\nYou can mark a type with the `const_fn` flag. In that case, its `new` and `try_new` functions will be declared as `const`:\n\n```rust\n#[nutype(\n    const_fn,\n    derive(Debug),\n    validate(greater_or_equal = -273.15),\n)]\npub struct Celsius(f64);\n```\n\nSince `Result::unwrap()` is not allowed in `const` contexts, we must manually handle the `Result` when creating constants. Any attempt to instantiate an invalid `Celsius` at compile time will trigger a compilation error:\n\n```rust\nconst FREEZING_POINT: Celsius = match Celsius::try_new(0.0) {\n    Ok(value) =\u003e value,\n    Err(_) =\u003e panic!(\"Invalid value\"),\n};\n```\n\nAlternatively, you can use a helper macro like this:\n\n```rust\nmacro_rules! nutype_const {\n    ($name:ident, $ty:ty, $value:expr) =\u003e {\n        const $name: $ty = match \u003c$ty\u003e::try_new($value) {\n            Ok(value) =\u003e value,\n            Err(_) =\u003e panic!(\"Invalid value\"),\n        };\n    };\n}\n\nnutype_const!(WATER_BOILING_POINT, Celsius, 100.0);\n```\n\nNote that `const` works only for stack allocated types.\nIf you are dealing with a heap allocated type (e.g. `String`) you should consider using `static` with [`LazyLock`](https://doc.rust-lang.org/beta/std/sync/struct.LazyLock.html).\n\n## Recipes\n\n### Obtaining a reference to the inner value\n\nThe function `.into_inner()` takes ownership of the newtype and returns its inner type. However, if you only need to borrow the inner value (rather than consume it), you can derive `AsRef`. This allows you to call `as_ref()` to obtain a reference to the underlying data:\n\n```rs\n#[nutype(derive(AsRef))]\nstruct Username(String);\n\nlet username = Username::new(\"Jack\");\nassert_eq!(username.as_ref(), \"Jack\");\n```\n\n### Derive `Default`\n\n```rs\n#[nutype(\n    derive(Default),\n    default = \"Anonymous\",\n)]\npub struct Name(String);\n```\n\n### Derive `Eq` and `Ord` on float types\n\nWith nutype it's possible to derive `Eq` and `Ord` if there is `finite` validation set.\nThe `finite` validation ensures that the valid value excludes `NaN`.\n\n```rs\n#[nutype(\n    validate(finite),\n    derive(PartialEq, Eq, PartialOrd, Ord),\n)]\npub struct Weight(f64);\n```\n\n\n## Breaking constraints with new_unchecked\n\nIt's discouraged, but it's possible to bypass the constraints by enabling `new_unchecked` crate feature and marking a type with `new_unchecked`:\n\n```rs\n#[nutype(\n    new_unchecked,\n    sanitize(trim),\n    validate(len_char_min = 8)\n)]\npub struct Name(String);\n\n// Yes, you're forced to use `unsafe` here, so everyone will point fingers at YOU.\nlet name = unsafe { Name::new_unchecked(\" boo \".to_string()) };\n\n// `name` violates the sanitization and validation rules!!!\nassert_eq!(name.into_inner(), \" boo \");\n```\n\n## Feature flags\n\n* `arbitrary` - enables derive of [`arbitrary::Arbitrary`](https://docs.rs/arbitrary/latest/arbitrary/trait.Arbitrary.html).\n* `new_unchecked` - enables generation of unsafe `::new_unchecked()` function.\n* `regex` - allows to use `regex = ` validation on string-based types. Note: your crate also has to explicitly have `regex` within its dependencies.\n* `serde` - integrations with [`serde`](https://crates.io/crates/serde) crate. Allows to derive `Serialize` and `Deserialize` traits.\n* `schemars08` - allows to derive [`JsonSchema`](https://docs.rs/schemars/0.8.12/schemars/trait.JsonSchema.html) trait of [schemars](https://crates.io/crates/schemars) crate. Note that at the moment validation rules are not respected.\n* `std` - enabled by default. Use `default-features = false` to disable.\n\n## When nutype is a good fit for you?\n\n* If you enjoy [newtype](https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction)\n  pattern and you like the idea of leveraging the Rust type system to enforce the correctness of the business logic.\n* If you want to use type system to hold invariants\n* If you're a DDD fan, nutype is a great helper to make your domain models even more expressive.\n* You want to prototype quickly without sacrificing quality.\n\n## When nutype is not that good?\n\n* You care too much about compiler time (nutype relies on heavy usage of proc macros).\n* You think metaprogramming is too much implicit magic.\n* IDEs may not be very helpful at giving you hints about proc macros.\n* Design of nutype may enforce you to run unnecessary validation (e.g. on loading data from DB), which may have a negative impact if you aim for extreme performance.\n\n## Support Ukrainian military forces\n\nToday I live in Berlin, I have the luxury to live a physically safe life.\nBut I am Ukrainian. The first 25 years of my life I spent in [Kharkiv](https://en.wikipedia.org/wiki/Kharkiv),\nthe second-largest city in Ukraine, 60km away from the border with russia. Today about [a third of my home city is destroyed](https://www.youtube.com/watch?v=ihoufBFSZds) by russians.\nMy parents, my relatives and my friends had to survive the artillery and air attack, living for over a month in basements.\n\nSome of them have managed to evacuate to EU. Some others are trying to live \"normal lives\" in Kharkiv, doing there daily duties.\nAnd some are at the front line right now, risking their lives every second to protect the rest.\n\nI encourage you to donate to [Charity foundation of Serhiy Prytula](https://prytulafoundation.org/en).\nJust pick the project you like and donate. This is one of the best-known foundations, you can watch a [little documentary](https://www.youtube.com/watch?v=VlmWqoeub1Q) about it.\nYour contribution to the Ukrainian military force is a contribution to my calmness, so I can spend more time developing the project.\n\nThank you.\n\n## Similar projects\n\n* [prae](https://github.com/teenjuna/prae) - A very similar crate that aims to solve the same problems but with slightly different approach.\n* [bounded-integer](https://github.com/Kestrer/bounded-integer) - Bounded integers for Rust.\n* [refinement](https://docs.rs/refinement/latest/refinement/) - Convenient creation of type-safe refinement types (based on generics).\n* [semval](https://github.com/slowtec/semval) - Semantic validation for Rust.\n* [validator](https://github.com/Keats/validator) - Simple validation for Rust structs (powered by macros).\n\n## License\n\nMIT © [Serhii Potapov](https://www.greyblake.com)\n","funding_links":[],"categories":["Rust","Libraries","\u003ca name=\"Rust\"\u003e\u003c/a\u003eRust"],"sub_categories":["Data structures"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreyblake%2Fnutype","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgreyblake%2Fnutype","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreyblake%2Fnutype/lists"}