| Crates.io | nodyn |
| lib.rs | nodyn |
| version | 0.2.2 |
| created_at | 2025-06-02 11:29:37.591956+00 |
| updated_at | 2025-08-28 11:48:16.626186+00 |
| description | Easy polymorphism with enums |
| homepage | |
| repository | https://github.com/franklaranja/nodyn |
| max_upload_size | |
| id | 1697868 |
| size | 179,923 |
Easy polymorphism with enums
nodyn provides a Rust macro for creating wrapper enums that
encapsulate a fixed set of types with automatic implementations
for From, TryFrom, and delegated methods or traits. This is
ideal for scenarios where you need to store values of different
types in a type-safe, zero-cost way, as an alternative to trait
objects.
"This is a perfectly good solution when our interchangeable items are a fixed set of types that we know when our code is compiled."
The Rust Programming Language
Create a simple enum wrapper for i32, String, and f64:
nodyn::nodyn! {
#[derive(Debug, PartialEq)]
pub enum Value {
i32,
String,
f64,
}
}
let values: Vec<Value> = vec![
42.into(), // Converts i32 to Value::I32
"hello".to_string().into(), // Converts String to Value::String
3.14.into(), // Converts f64 to Value::F64
];
for value in values {
match value {
Value::I32(n) => println!("Integer: {}", n),
Value::String(s) => println!("String: {}", s),
Value::F64(f) => println!("Float: {}", f),
}
}
i32, String, or custom structs with CamelCase naming.From<T> for each variant and TryFrom<Enum>
with the impl TryInto directive.count, types, and type_name
methods with impl introspection.Vec<Enum> wrapper with a vec!-like macro
and variant-specific methods (e.g., first_i32, count_string) via
vec.Use impl directives to enable features explicitly (e.g., impl TryInto is_as). Cargo features (try_into, is_as, introspection) are
deprecated but supported for backward compatibility. See the Feature Flags section
for details.
Use the vec feature to create a polymorphic Vec with variant-specific
methods:
nodyn::nodyn! {
#[derive(Debug, Clone)]
pub enum Item {
i32, // Gold coins
String, // Weapon names
f64, // Health potions (liters)
}
vec Inventory;
}
let mut inventory = inventory![100, "sword".to_string(), 0.5, "axe".to_string()];
// Add more gold
inventory.push(50);
// Check for weapons in the inventory
assert!(inventory.any_string());
// Total gold coins
let total_gold = inventory.iter_i32().sum::<i32>();
assert_eq!(total_gold, 150);
// Get a potion
if let Some(potion) = inventory.first_f64() {
println!("Found potion: {} liters", potion); // Prints: 0.5 liters
}
See the Polymorphic Vec section in the Documentation
nodyn::nodyn! {
enum Container { String, Vec<u8> }
impl {
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn clear(&mut self);
}
}
let mut container: Container = "hello".to_string().into();
assert_eq!(container.len(), 5);
assert!(!container.is_empty());
container.clear();
assert!(container.is_empty());
Delegate entire traits when all wrapped types implement them:
use std::fmt::{self, Display};
// All wrapped types implement Display
nodyn::nodyn! {
enum Displayable { i32, String, f64 }
impl Display {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}
vec Displayables;
}
let values = displayables![42, "hello".to_string(), 3.14];
for val in values {
println!("{}", val); // Uses delegated Display implementation
}
This example creates a JSON-like data structure with nested arrays, showcasing trait delegation and Polymorphic Vec features:
use std::fmt;
#[derive(Debug, Clone)]
pub struct Null;
impl fmt::Display for Null {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "null")
}
}
#[derive(Debug, Clone)]
pub struct JsonArray(JsonValueVec);
impl fmt::Display for JsonArray {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = self.0.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ");
write!(f, "[{s}]")
}
}
nodyn::nodyn! {
#[derive(Debug, Clone)]
pub enum JsonValue {
Null,
bool,
f64,
String,
JsonArray,
}
vec;
impl fmt::Display {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}
impl {
pub const fn json_type_name(&self) -> &'static str {
match self {
Self::Null(_) => "null",
Self::Bool(_) => "boolean",
Self::F64(_) => "number",
Self::String(_) => "string",
Self::JsonArray(_) => "array",
}
}
}
}
let mut values = JsonValueVec::default();
values.push(Null);
values.push(true);
values.push(42.0);
values.push("hello".to_string());
values.push(JsonArray(json_value_vec![Null, false, 33.0]));
for val in &values {
println!("{}: {}", val.json_type_name(), val);
}
## Installation
Add `nodyn` to your `Cargo.toml`:
```toml
[dependencies]
nodyn = "0.2.0"
| Feature | nodyn | enum_dispatch | sum_type | Box |
|---|---|---|---|---|
| Runtime Cost | Zero | Zero | Zero | Heap allocation |
| Trait Delegation | ✅ Yes | ✅ Scoped only | ❌ No | ✅ Yes |
| Method Delegation | ✅ Yes | ❌ No | ❌ No | ❌ No |
| Type Introspection | ✅ Built-in | ❌ No | ❌ No | ❌ No |
| Vec Wrapper | ✅ Yes | ❌ No | ❌ No | ❌ No |
| Compile-Time Known | Required | Required | Required | Not required |
| Memory Overhead | Discriminant only | Discriminant only | Discriminant only | Pointer + vtable |
Contributions are welcome! Check out the GitHub repository for issues, feature requests, or to submit pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.