A comprehensive Aspect-Oriented Programming (AOP) framework for Rust, bringing the power of cross-cutting concerns modularization to the Rust ecosystem.
📖 Read the Book - Comprehensive guide with 11 chapters covering motivation, usage, architecture, and advanced topics.
Aspect-Oriented Programming helps you modularize cross-cutting concerns - functionality that cuts across multiple parts of your application:
- Logging: Track function entry/exit across your codebase
- Performance Monitoring: Measure execution time automatically
- Caching: Add memoization without cluttering business logic
- Security: Enforce authorization declaratively
- Transactions: Manage database transactions transparently
- Retry Logic: Add resilience patterns without boilerplate
- Validation: Enforce constraints before function execution
Add to your Cargo.toml:
[dependencies]
aspect-core = "0.1"
aspect-macros = "0.1"
aspect-std = "0.1" # Optional: production-ready aspectsMinimum Supported Rust Version (MSRV): 1.70+
The aspect-std crate provides 8 battle-tested aspects ready for production use:
| Aspect | Purpose | Example Use Case |
|---|---|---|
| LoggingAspect | Entry/exit logging | Trace function calls with timestamps |
| TimingAspect | Performance monitoring | Measure execution time, warn on slow functions |
| CachingAspect | Memoization | Cache expensive computations |
| MetricsAspect | Metrics collection | Track call counts, latency distributions |
| RateLimitAspect | Rate limiting | Prevent API abuse with token bucket |
| CircuitBreakerAspect | Fault tolerance | Handle service failures gracefully |
| AuthorizationAspect | Access control | Enforce role-based permissions (RBAC) |
| ValidationAspect | Input validation | Validate function arguments with custom rules |
use aspect_std::*;
// Logging with timestamps
#[aspect(LoggingAspect::new())]
fn process_order(order: Order) -> Result<Receipt, Error> {
// Business logic
}
// Rate limiting (100 calls per minute)
#[aspect(RateLimitAspect::new(100, Duration::from_secs(60)))]
pub fn api_endpoint(request: Request) -> Response {
handle_request(request)
}
// Authorization with role-based access control
#[aspect(AuthorizationAspect::require_role("admin", get_current_user_roles))]
pub fn delete_user(user_id: u64) -> Result<(), Error> {
database::delete_user(user_id)
}
// Circuit breaker (opens after 5 failures, retries after 30s)
#[aspect(CircuitBreakerAspect::new(5, Duration::from_secs(30)))]
pub fn call_external_service(url: &str) -> Result<Response, Error> {
reqwest::blocking::get(url)?.json()
}Define an aspect and apply it to your functions:
use aspect_core::prelude::*;
use aspect_macros::aspect;
// Define an aspect
#[derive(Default)]
struct Logger;
impl Aspect for Logger {
fn before(&self, ctx: &JoinPoint) {
println!("[ENTRY] {}", ctx.function_name);
}
fn after(&self, ctx: &JoinPoint, _result: &dyn std::any::Any) {
println!("[EXIT] {}", ctx.function_name);
}
}
// Apply it to your functions
#[aspect(Logger)]
fn fetch_user(id: u64) -> Result<User, Error> {
// Your business logic here
database::query_user(id)
}- ✅ Procedural Macros: Works with stable Rust, no custom toolchain needed
- ✅ Multiple Advice Types:
before,after,after_error,around - ✅ Aspect Composition: Stack multiple aspects on a single function
- ✅ Async Support: Full support for async functions
- ✅ Generic Functions: Preserves generics and lifetimes
- ✅ Zero Cost Abstractions: <10ns overhead for simple aspects
- ✅ Production-Ready Aspects: 8 battle-tested aspects in
aspect-std - ✅ Well-Tested: 108+ tests across all crates
- ✅ Comprehensive Benchmarks: Performance validated with criterion
An aspect is a module that encapsulates a cross-cutting concern. Implement the Aspect trait to define your aspect's behavior:
pub trait Aspect: Send + Sync {
fn before(&self, ctx: &JoinPoint) {}
fn after(&self, ctx: &JoinPoint, result: &dyn Any) {}
fn after_error(&self, ctx: &JoinPoint, error: &dyn Error) {}
fn around(&self, pjp: ProceedingJoinPoint) -> Result<Box<dyn Any>, Box<dyn Error>> {
pjp.proceed()
}
}A joinpoint represents a point in your program's execution where an aspect can be applied (e.g., function call). The JoinPoint struct provides context:
pub struct JoinPoint {
pub function_name: &'static str,
pub module_path: &'static str,
pub location: Location,
}Advice is the action taken by an aspect at a joinpoint:
before: Runs before the function executesafter: Runs after successful executionafter_error: Runs if the function returns an erroraround: Wraps the entire function execution
#[derive(Default)]
struct Logger;
impl Aspect for Logger {
fn before(&self, ctx: &JoinPoint) {
println!("[{}:{}] Entering {}",
ctx.location.file,
ctx.location.line,
ctx.function_name);
}
}
#[aspect(Logger)]
fn process_data(input: &str) -> Result<String, Error> {
Ok(input.to_uppercase())
}struct Timer;
impl Aspect for Timer {
fn around(&self, pjp: ProceedingJoinPoint) -> Result<Box<dyn Any>, Box<dyn Error>> {
let start = Instant::now();
let result = pjp.proceed();
let elapsed = start.elapsed();
println!("{} took {:?}", pjp.context().function_name, elapsed);
result
}
}
#[aspect(Timer)]
fn expensive_computation(n: u64) -> u64 {
// Heavy computation
fibonacci(n)
}Stack aspects to compose behavior:
#[aspect(Logger)]
#[aspect(Timer)]
#[aspect(Cache)]
fn fetch_data(key: String) -> Result<Data, Error> {
// Business logic
}Aspects execute in order: Cache → Timer → Logger → function → Logger → Timer → Cache
See the aspect-examples/ directory for complete working examples:
- logging.rs: Entry/exit logging with timestamps
- timing.rs: Performance measurement and slow function warnings
- caching.rs: Memoization and cache monitoring
- advanced_aspects.rs: Rate limiting, circuit breakers, authorization, validation
- api_server.rs: Complete REST API with CRUD operations
- More patterns coming soon: Distributed tracing, async aspects, custom pointcuts
# Run the API server example
cargo run --example api_server
# Run advanced aspects demo
cargo run --example advanced_aspects
# Run all examples
cargo run --example logging
cargo run --example timing
cargo run --example cachingaspect-rs/
├── aspect-core/ # Core traits and types (Aspect, JoinPoint, etc.)
├── aspect-macros/ # Procedural macros (#[aspect] attribute)
├── aspect-std/ # Production-ready aspects library (8 aspects)
├── aspect-runtime/ # Runtime utilities and registry
├── aspect-examples/ # Comprehensive examples and patterns
├── aspect-driver/ # rustc-driver integration
└── cargo-aspect/ # Cargo plugin for automatic weaving
The #[aspect] macro transforms your function at compile time:
// You write:
#[aspect(LoggingAspect::new())]
fn process_data(input: &str) -> Result<String, Error> {
Ok(input.to_uppercase())
}
// Macro expands to:
fn __aspect_original_process_data(input: &str) -> Result<String, Error> {
Ok(input.to_uppercase())
}
fn process_data(input: &str) -> Result<String, Error> {
let ctx = JoinPoint {
function_name: "process_data",
module_path: module_path!(),
location: Location { file: file!(), line: line!() },
};
// Before advice
LoggingAspect::new().before(&ctx);
// Execute original function
let result = __aspect_original_process_data(input);
// After advice
LoggingAspect::new().after(&ctx, &result);
result
}This approach provides:
- Zero runtime overhead when aspects are no-ops
- Compile-time safety - all aspect code is type-checked
- Clean generated code - easy to debug with
cargo expand
Benchmarked on AMD Ryzen 9 5950X (see BENCHMARKS.md):
| Scenario | Overhead | Target |
|---|---|---|
| No-op aspect | ~2ns | <10ns ✅ |
| Simple logging | ~8ns | <10ns ✅ |
| Multiple aspects | ~15ns | <20ns ✅ |
| Complex caching | Variable | Depends on cache hit rate |
All performance targets met!
- Quick Start Guide - Get up and running in 5 minutes
- API Documentation - Complete API reference
- Examples - 10 real-world code examples
- Migration Guide - Migrate from manual code, decorators, middleware, or other AOP frameworks
- Comparison Guide - Compare aspect-rs with AspectJ, PostSharp, and alternative approaches
- Benchmarks Guide - Performance analysis and optimization techniques
- Optimization Guide - Strategies for achieving <5% overhead
- Changelog - Complete version history
- Contributing - Contribution guidelines
- Release Checklist - Publication and release process
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.
Inspired by AspectJ (Java), Spring AOP, and the Rust community's desire for clean, modular code.