Crates.io | arc-handle |
lib.rs | arc-handle |
version | 1.0.0 |
created_at | 2025-08-08 02:49:35.130281+00 |
updated_at | 2025-08-08 02:49:35.130281+00 |
description | Proc macro for generating Arc-based handle wrappers for traits |
homepage | |
repository | https://gitlab.com/akarifur/arc-handle |
max_upload_size | |
id | 1786127 |
size | 25,470 |
A Rust procedural macro crate for generating Arc-based handle wrappers for traits, enabling easy trait object sharing across threads.
The #[arc_handle]
attribute macro transforms a trait definition into a thread-safe handle struct that wraps trait implementations in Arc<dyn Trait + Send + Sync>
. This pattern is useful when you need to share trait objects across multiple threads or async tasks.
Clone
, Send
, and Sync
Add this to your Cargo.toml
:
[dependencies]
arc-handle = "1.0.0"
use arc_handle::arc_handle;
#[arc_handle]
pub trait Calculator {
fn add(&self, a: i32, b: i32) -> i32;
fn multiply(&self, a: i32, b: i32) -> i32;
}
// Implementation
struct BasicCalculator;
impl CalculatorImpl for BasicCalculator {
fn add(&self, a: i32, b: i32) -> i32 {
a + b
}
fn multiply(&self, a: i32, b: i32) -> i32 {
a * b
}
}
// Usage
fn main() {
let calc = Calculator::new(BasicCalculator);
// Can be cloned and shared across threads
let calc_clone = calc.clone();
println!("2 + 3 = {}", calc.add(2, 3));
println!("2 * 3 = {}", calc_clone.multiply(2, 3));
}
use arc_handle::arc_handle;
use async_trait::async_trait;
#[arc_handle]
#[async_trait]
pub trait DataProcessor {
async fn process_data(&self, data: Vec<u8>) -> Result<String, Box<dyn std::error::Error>>;
fn get_name(&self) -> &str;
}
struct JsonProcessor {
name: String,
}
#[async_trait]
impl DataProcessorImpl for JsonProcessor {
async fn process_data(&self, data: Vec<u8>) -> Result<String, Box<dyn std::error::Error>> {
// Process data asynchronously
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
Ok(String::from_utf8(data)?)
}
fn get_name(&self) -> &str {
&self.name
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let processor = DataProcessor::new(JsonProcessor {
name: "JSON Processor".to_string(),
});
let data = b"Hello, World!".to_vec();
let result = processor.process_data(data).await?;
println!("Processed: {}", result);
println!("Processor: {}", processor.get_name());
Ok(())
}
When you apply #[arc_handle]
to a trait:
TraitImpl
Arc<dyn TraitImpl + Send + Sync>
new()
and from_boxed()
methods are generated for easy instantiationFor a trait named MyTrait
, the macro generates:
// Original trait renamed
pub trait MyTraitImpl {
// ... original methods
}
// Generated handle struct
#[derive(Clone)]
pub struct MyTrait {
inner: std::sync::Arc<dyn MyTraitImpl + Send + Sync>,
}
impl MyTrait {
pub fn new(inner: impl MyTraitImpl + Send + Sync + 'static) -> Self { /* ... */ }
pub fn from_boxed(inner: Box<dyn MyTraitImpl + Send + Sync>) -> Self { /* ... */ }
// Delegating methods for each trait method
// ...
}
#[async_trait]
from the async-trait
crateLicensed under either of
at your option.
Contributions are welcome! Please feel free to submit a Pull Request.