strong-type
strong-type is a Rust crate that offers macros to easily create strongly typed and named primitive and string types. Strong typing helps in making code more expressive and less prone to errors, ensuring that each type is used in its intended way.
use StrongType;
;
let timestamp = new;
println!; // Timestamp(1701620628123456789)
Features
-
no_stdcompatible: Works inno_stdenvironments with optionalallocsupport forStringtypes. -
Derive trait
StrongType: Create a named strong type.- The macro automatically implements
Clone,Debug,PartialEq, andPartialOrd, and will conditionally addCopy,Default,Eq,Ord, andHashwhen appropriate.SendandSyncare automatically derived by Rust when the wrapped type implements them. - Every generated type exposes ergonomic helpers such as
new,const_new,into_inner,as_ref, andas_mut, plus blanketAsRef/AsMutimplementations so you can seamlessly borrow the inner value. - Conditionally, based on the underlying data type, traits like
Copy,Eq,Ord,Hashmay also be implemented. For primitive data types likei32orbool, these additional traits will be automatically included. - Numeric types, both integer and floating-point, also implement constants
MIN,MAX,INFINITY,NEG_INFINITY, andZERO. Additionally, for floating-point types,NANis implemented.
- The macro automatically implements
-
Attributes:
- Adding the following attributes to
#[strong_type(...)]allows for additional features:auto_operators: Automatically implements relevant arithmetic (for numeric types) or logical (for boolean types) operators with all ownership variants (owned,&Self, etc.).- Use
auto_operators = "delegated"when you want all ownership combinations but prefer smaller binaries (requires the primitive type to beCopy); delegated mode routes operator bodies through shared helpers instrong_type::delegation, trimming monomorphization in debug builds at the cost of a small inlining opportunity. - Use
auto_operators = "minimal"for a lightweight version that generates only owned-value operations, reducing binary size while maintaining core functionality. - Use
auto_operators = "full"or justauto_operatorsfor the complete set of operator implementations.
- Use
addable: Automatically implements theAdd,Sub, and other relevant traits. The attribute is a strict subset ofauto_operators.scalable: Automatically implements theMul,Div,Rem, and other relevant traits between a strong typed struct and its primitive type. Note that the attribute is not a subset ofauto_operators.custom_display: Allows users to manually implement theDisplaytrait, providing an alternative to the default display format.conversion: Automatically implementsFrom/Intofor owned and borrowed variants of the underlying type, making it easy to cross the boundary when needed. This is optional since conversion may make strong types less distinct.underlying: Specifies the underlying primitive type for nested strong types.
- Adding the following attributes to
Installation
Add strong-type to your Cargo.toml:
[]
= "1.1"
no_std Support
This crate supports no_std environments. By default, the std feature is enabled.
# Default (with std)
= "1.1"
# no_std with alloc (for String support)
= { = "1.1", = false, = ["alloc"] }
# no_std without alloc (primitives only)
= { = "1.1", = false }
| Configuration | Primitives | String |
|---|---|---|
std or alloc |
✓ | ✓ |
| (none) | ✓ | ✗ |
Note:
stdandallocare functionally equivalent for this crate. Thestdfeature (enabled by default) exists for convention and future compatibility.
When using String in no_std mode, import it from the crate:
use ;
;
Supported underlying types:
- Integer types:
i8,i16,i32,i64,i128,isize - Unsigned integer types:
u8,u16,u32,u64,u128,usize - Floating-point types:
f32,f64 - Boolean type:
bool charString- Strong types of the above types
Examples
Creating a named strong type:
With a private field:
use StrongType;
;
let tag = new;
const TAG: Tag = const_new;
With a public field:
use StrongType;
;
let timestamp = Timestamp;
println!; // Timestamp(1701620628123456789)
Demonstrating type distinctiveness:
use StrongType;
use Any;
;
;
let x = new;
let y = new;
let z = new;
assert_eq!; // Same type: Second
assert_ne!; // Different types: Second versus Minute
Utilizing Hashability:
use HashSet;
;
let mut map = new;
map.insert;
map.insert;
assert_eq!;
Named integer type with arithmetic operations:
use StrongType;
;
let x = new;
let y = new;
let z = default;
assert_eq!;
assert_eq!;
assert_eq!;
assert!;
assert!;
assert_eq!;
;
let x = new;
assert_eq!;
Minimal operators for reduced binary size:
use StrongType;
// Full mode: generates operators for all ownership combinations
// (Type + Type, Type + &Type, &Type + Type, &Type + &Type)
// or auto_operators = "full"
;
let x = new;
let y = new;
assert_eq!; // Works with references
// Minimal mode: generates only owned operations for smaller binary size
;
let x = new;
let y = new;
assert_eq!; // Works with owned values
// assert_eq!(&x + &y, MinimalPrice(30)); // Won't compile - references not supported
// Minimal mode still supports:
// - All basic operators: +, -, *, /, %
// - Assignment operators: +=, -=, *=, /=, %=
// - Negation: -x
// - Iterator traits: Sum, Product
Delegated operators for shared codegen
use StrongType;
;
let x = new;
let y = new;
assert_eq!; // All ownership variants still compile
Delegated mode emits the full operator surface but forwards every body to small helpers in strong_type::delegation. This keeps ergonomics identical to auto_operators = "full" while trimming monomorphization-heavy code, typically shrinking debug binaries by 30-50% versus the full mode. Because those helpers are marked #[inline(never)], expect a tiny throughput regression when micro-benchmarked (<2% in our perf examples), which is usually offset by faster builds and smaller artifacts.
Named bool type with logical operations:
use StrongType;
;
let x = new;
let y = new;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
Custom display implementation with custom_display:
use ;
use StrongType;
;
println!; // "Second(2.72)"
println!; // "Second { value: 2.718281828459045 }"
Nested strong types:
;
;
;
Caveats:
- When using
#[derive(StrongType)], the traitsEqandPartialEqare implemented withimpl. As a result,StructuralEqandStructuralPartialEqremain unimplemented, preventing pattern matching with strong-typed primitives. #[strong_type(scalable)]does not work for nested strong types.