Universal structural types for Rust - bringing TypeScript's structural typing, Ruby's expressiveness, and Elixir's concurrency to Rust.
- π Structural Typing: TypeScript-like duck typing with compile-time safety
- πΊοΈ EnumMap: Efficient enum-indexed data structures with O(1) access
- π Actor System: Elixir OTP-inspired actors with supervision trees
- π― Pattern Matching: Powerful pattern matching on structural data
- π‘ Fault Tolerance: Supervisor trees for self-healing systems
- β‘ High Performance: Zero-cost abstractions with Rust's performance
- π§© Modular Design: Use only what you need
Add to your Cargo.toml
:
[dependencies]
structural = "0.1"
use structural::prelude::*;
// Create structural data (like TypeScript objects)
let user_data = shape_map! {
"name" => Shape::Str("Alice".to_string()),
"age" => Shape::Num(30.0),
"active" => Shape::Bool(true),
"preferences" => shape_map! {
"theme" => Shape::Str("dark".to_string()),
"notifications" => Shape::Bool(true)
}
};
// Duck typing for structural access
let duck = Duck::new(&user_data);
let name: String = duck.get("name")?;
let theme: String = duck.index(["preferences", "theme"])?.to()?;
println!("User {} prefers {} theme", name, theme);
use structural::prelude::*;
use structural_derive::EnumVariants;
#[derive(EnumVariants)]
enum Color { Red, Green, Blue }
// Create enum-indexed map (like TypeScript mapped types)
let colors = construct_enummap::<Color, String, _>(|variant| {
match variant {
"Red" => "#FF0000".to_string(),
"Green" => "#00FF00".to_string(),
"Blue" => "#0000FF".to_string(),
_ => "#000000".to_string(),
}
});
println!("Red color: {}", colors[&Color::Red]);
use structural::prelude::*;
use structural::pattern::*;
let api_response = shape_variant!("Ok", shape_map! {
"data" => Shape::Str("Success!".to_string()),
"timestamp" => Shape::Num(1642681800.0)
});
let pattern = variant("Ok").with_inner(
map().field("data".to_string(), var("content"))
.field("timestamp".to_string(), var("time"))
);
if pattern.matches(&api_response) {
let bindings = pattern.extract(&api_response)?;
let content: String = bindings.get_as("content")?;
println!("Success: {}", content);
}
use structural::prelude::*;
use std::pin::Pin;
struct CounterActor { count: i32 }
impl Actor for CounterActor {
fn handle(&mut self, envelope: Envelope, _ctx: &mut ActorContext)
-> Pin<Box<dyn Future<Output = Result<(), ActorError>> + Send + '_>>
{
Box::pin(async move {
let duck = Duck::new(&envelope.message);
match duck.try_get::<String>("action").as_deref() {
Some("increment") => {
self.count += 1;
let response = shape_map! {
"count" => Shape::Num(self.count as f64)
};
envelope.reply(response)?;
}
_ => {}
}
Ok(())
})
}
}
#[tokio::main]
async fn main() -> Result<(), ActorError> {
let system = ActorSystem::new("example".to_string());
let counter = system.spawn_actor(
Some("counter".to_string()),
CounterActor { count: 0 }
).await?;
let msg = shape_map! { "action" => Shape::Str("increment".to_string()) };
let response = counter.ask(msg, Duration::from_secs(1)).await?;
let duck = Duck::new(&response);
println!("Count: {}", duck.get::<f64>("count")?);
system.stop_all().await?;
Ok(())
}
use structural::prelude::*;
use structural_actor::*;
let supervisor = SupervisorBuilder::new()
.strategy(SupervisorStrategy::OneForOne)
.restart_intensity(3, Duration::from_secs(5))
.child(
ChildSpec::new("worker1".to_string()),
|| async { WorkerActor::new("worker-1") }
)
.child(
ChildSpec::new("worker2".to_string()),
|| async { WorkerActor::new("worker-2") }
)
.build();
let system = ActorSystem::new("fault_tolerant".to_string());
let supervisor_ref = system.spawn_actor(Some("supervisor".to_string()), supervisor).await?;
// Children will be automatically restarted if they fail
// OneForOne strategy means only failed child gets restarted
Structural-RS is organized into modular crates:
structural-core
: Core types (Shape
,Duck
,EnumMap
, pattern matching)structural-derive
: Procedural macros (EnumVariants
,Shape
)structural-actor
: Actor system with supervision treesstructural
: Main crate that re-exports everything
Run the full feature showcase:
cargo run --bin structural_showcase --package comprehensive-example
This demonstrates:
- Structural typing with complex nested data
- EnumMap usage with different enum types
- Pattern matching on various data structures
- Actor system with message passing
- Supervisor trees with fault tolerance
A real-world example implementing a chat server:
cargo run --bin chat_server --package comprehensive-example
Features:
- WebSocket connections
- Room management
- Message broadcasting
- Actor-based architecture
- Fault-tolerant supervision
- Structural typing: Access object properties dynamically with type safety
- Duck typing: "If it walks like a duck..." - structural compatibility
- JSON handling: Parse and manipulate JSON with TypeScript-like syntax
- Dynamic data: Flexible data structures with hash-like access
- Expressiveness: Intuitive APIs for data manipulation
- Metaprogramming: Dynamic behavior with compile-time safety
- Actor model: Lightweight processes with message passing
- Supervision trees: Fault-tolerant systems with automatic recovery
- Pattern matching: Destructure data with powerful pattern syntax
- OTP patterns: GenServer-like actors with lifecycle management
- Network protocols: Parse and handle protocol messages structurally
- Configuration: Dynamic configuration with validation
- APIs: Build HTTP APIs with structural request/response handling
- Microservices: Actor-based service architecture
use structural_derive::{EnumVariants, Shape};
#[derive(EnumVariants, Debug)]
enum Status { Active, Inactive, Pending }
#[derive(Shape, Debug)]
struct User {
name: String,
age: u32,
status: Status,
}
// Automatically implements ToShape and FromShape
let user = User {
name: "Bob".to_string(),
age: 25,
status: Status::Active
};
let shape = user.to_shape();
let duck = Duck::new(&shape);
println!("User name: {}", duck.get::<String>("name")?);
use structural::pattern::*;
// Complex nested pattern matching
let pattern = map()
.field("user".to_string(),
map().field("profile".to_string(),
map().field("settings".to_string(),
array().element(var("first_setting"))
.element(literal("dark_mode"))
)
)
);
// Matches deeply nested structures
if pattern.matches(&complex_data) {
let bindings = pattern.extract(&complex_data)?;
// Access extracted variables
}
struct TypedCounterActor { count: i32 }
#[derive(Debug)]
enum CounterMessage {
Increment,
Decrement,
GetCount,
}
impl ToShape for CounterMessage { /* ... */ }
impl FromShape for CounterMessage { /* ... */ }
impl TypedActor<CounterMessage> for TypedCounterActor {
fn handle_message(&mut self, message: CounterMessage, ctx: &mut ActorContext)
-> Pin<Box<dyn Future<Output = Result<(), ActorError>> + Send + '_>>
{
Box::pin(async move {
match message {
CounterMessage::Increment => self.count += 1,
CounterMessage::Decrement => self.count -= 1,
CounterMessage::GetCount => {
// Send response...
}
}
Ok(())
})
}
}
- Zero-cost abstractions: No runtime overhead for structural typing
- Efficient EnumMap: O(1) access time, minimal memory overhead
- Lightweight actors: Thousands of actors with minimal memory usage
- Fast pattern matching: Compile-time optimized pattern matching
- Async/await: Built on Tokio for high-performance async I/O
# Build all crates
cargo build --workspace
# Run tests
cargo test --workspace
# Run examples
cargo run --example structural_showcase
# Unit tests
cargo test --lib
# Integration tests
cargo test --test integration
# Property-based tests
cargo test --features proptest
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
- Performance optimizations: Micro-benchmarks and optimizations
- More pattern types: Additional pattern matching constructs
- Protocol implementations: HTTP, WebSocket, etc.
- Validation framework: Schema validation for shapes
- Documentation: Examples, guides, and API docs
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.
Inspired by:
- TypeScript: Structural typing and duck typing
- Ruby: Dynamic expressiveness and metaprogramming
- Elixir/OTP: Actor model and fault tolerance
- Rust: Zero-cost abstractions and memory safety