# arc-handle
A Rust procedural macro crate for generating Arc-based handle wrappers for traits, enabling easy trait object sharing across threads.
## Overview
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.
## Features
- **Thread-safe sharing**: Generated handles implement `Clone`, `Send`, and `Sync`
- **Async/await support**: Properly handles both sync and async trait methods
- **Zero-cost abstraction**: Minimal overhead over direct Arc usage
- **Easy integration**: Simple attribute macro application
## Installation
Add this to your `Cargo.toml`:
```toml
[dependencies]
arc-handle = "1.0.0"
```
## Usage
### Basic Example
```rust
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));
}
```
### Async Example
```rust
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(())
}
```
## How It Works
When you apply `#[arc_handle]` to a trait:
1. **Trait Transformation**: The original trait is renamed to `TraitImpl`
2. **Handle Generation**: A new struct with the original trait name is created
3. **Method Delegation**: All trait methods are implemented on the handle, delegating to the inner `Arc<dyn TraitImpl + Send + Sync>`
4. **Constructor Methods**: `new()` and `from_boxed()` methods are generated for easy instantiation
### Generated Code Structure
For a trait named `MyTrait`, the macro generates:
```rust
// 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
// ...
}
```
## Requirements
- Rust 2024 edition or later
- For async traits, use with `#[async_trait]` from the `async-trait` crate
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.