1 unstable release
| 0.1.0 | Nov 25, 2025 |
|---|
#2829 in Rust patterns
41KB
677 lines
repr_cast
A Rust procedural macro library that enhances fieldless enums with proper conversions between enum variants and their integer representation types.
Features
- Bidirectional conversions: Convert enums to integers and integers back to enums
- Type-safe: Uses
TryFromfor fallible conversions from integers - Const-friendly: Generated methods work in const contexts
- Ergonomic: Supports both explicit and implicit discriminants
- Error handling: Provides descriptive error types for invalid conversions
- Zero overhead: All conversions are inlined and compile to efficient code
Installation
Add this to your Cargo.toml:
[dependencies]
repr_cast = "0.1"
Usage
Simply add the #[repr_cast(T)] attribute to your fieldless enum, where T is the integer type you want to use:
use repr_cast::repr_cast;
#[derive(Debug, PartialEq)]
#[repr_cast(u8)]
enum Status {
Pending = 0,
Active = 1,
Completed = 2,
}
Generated API
The macro generates the following for your enum:
#[repr(T)]- Ensures the enum has the specified memory representationFrom<Enum> for T- Converts owned enum to integerFrom<&Enum> for T- Converts enum reference to integerTryFrom<T> for Enum- Converts owned integer to enum (returnsEnumConversionErrorfor invalid values)TryFrom<&T> for Enum- Converts integer reference to enumEnum::from_repr(value: T) -> Option<Enum>- Safe conversion from integerEnum::as_repr(&self) -> T- Converts enum to integerEnumConversionError- Error type for failed conversions
Examples
Basic conversion
use repr_cast::repr_cast;
use std::convert::TryFrom;
#[derive(Debug, PartialEq)]
#[repr_cast(u8)]
enum Status {
Pending = 0,
Active = 1,
Completed = 2,
}
// Enum to integer
let status = Status::Active;
let value: u8 = status.into(); // or status.as_repr()
assert_eq!(value, 1);
// Integer to enum (fallible)
let status = Status::try_from(1).unwrap();
assert_eq!(status, Status::Active);
// Safe conversion
if let Some(status) = Status::from_repr(2) {
println!("Got status: {:?}", status);
}
Signed integers
#[derive(Debug, PartialEq)]
#[repr_cast(i32)]
enum Priority {
Low = -1,
Normal = 0,
High = 1,
Critical = 100,
}
let priority = Priority::Low;
let value: i32 = priority.into();
assert_eq!(value, -1);
Reference conversions
#[derive(Debug, PartialEq)]
#[repr_cast(u8)]
enum Status {
Pending = 0,
Active = 1,
Completed = 2,
}
let status = Status::Active;
// Convert owned enum (consumes the enum)
let value1: u8 = status.into();
// Convert reference (doesn't consume the enum)
let status = Status::Active;
let value2: u8 = (&status).into();
assert_eq!(value2, 1);
// Can use the enum after reference conversion
println!("Status: {:?}, Value: {}", status, value2);
// TryFrom works with references too
let code = 1u8;
let status = Status::try_from(&code).unwrap();
assert_eq!(status, Status::Active);
Implicit discriminants
#[derive(Debug, PartialEq)]
#[repr_cast(u16)]
enum Color {
Red, // 0
Green, // 1
Blue, // 2
}
assert_eq!(Color::Green.as_repr(), 1);
Const expressions
const ERROR_BASE: u16 = 400;
const SERVER_ERROR_BASE: u16 = 500;
#[derive(Debug, PartialEq)]
#[repr_cast(u16)]
enum ErrorCode {
Success = 0,
InvalidInput, // 1
BadRequest = ERROR_BASE, // 400
Forbidden, // 401
InternalError = SERVER_ERROR_BASE, // 500
ServiceUnavailable, // 501
}
assert_eq!(ErrorCode::BadRequest.as_repr(), ERROR_BASE);
assert_eq!(ErrorCode::try_from(500).unwrap(), ErrorCode::InternalError);
Const contexts
#[derive(Debug, PartialEq)]
#[repr_cast(u8)]
enum Flag {
Off = 0,
On = 1,
}
const FLAG_VALUE: u8 = Flag::On.as_repr();
const MAYBE_FLAG: Option<Flag> = Flag::from_repr(1);
Error handling
use std::convert::TryFrom;
#[derive(Debug, PartialEq)]
#[repr_cast(u8)]
enum Status {
Pending = 0,
Active = 1,
}
match Status::try_from(99) {
Ok(status) => println!("Valid: {:?}", status),
Err(err) => {
println!("Error: {}", err); // "unknown Status variant: 99"
println!("Invalid value: {}", err.0); // 99
}
}
Supported Integer Types
The macro works with all Rust integer types:
- Unsigned:
u8,u16,u32,u64,u128,usize - Signed:
i8,i16,i32,i64,i128,isize
Requirements
- The enum must be fieldless (all variants must be unit variants)
- The enum cannot have generics (currently)
- All discriminant values must fit in the specified integer type
The macro provides clear error messages if these requirements are not met.
Comparison with #[repr(i*)] / #[repr(u*)]
Rust's built-in #[repr(i*)] and #[repr(u*)] attributes only control memory layout. They don't provide any conversion methods. This library:
- ✅ Includes
#[repr(T)]automatically - ✅ Adds safe conversion methods
- ✅ Implements standard traits (
From,TryFrom) - ✅ Supports both owned and reference conversions
- ✅ Provides const-compatible functions
- ✅ Handles complex discriminant expressions
- ✅ Includes proper error types with helpful messages
License
This project is dual-licensed under MIT or Apache-2.0, at your option.
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
Dependencies
~135–530KB
~13K SLoC