Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Vision: Annotation-Free AOP

Phase 3 represents the culmination of aspect-rs: achieving AspectJ-style automatic aspect weaving in Rust. This chapter explains the vision behind annotation-free AOP and why it matters.

The Dream

Imagine writing pure business logic with zero aspect-related code:

#![allow(unused)]
fn main() {
// Pure business logic - NO annotations!
pub fn fetch_user(id: u64) -> User {
    database::get(id)
}

pub fn save_user(user: User) -> Result<()> {
    database::save(user)
}

pub fn delete_user(id: u64) -> Result<()> {
    database::delete(id)
}

fn internal_helper() -> i32 {
    42
}
}

Then applying aspects automatically via build configuration:

aspect-rustc-driver \
    --aspect-pointcut "execution(pub fn *(..))" \
    --aspect-type "LoggingAspect" \
    main.rs

Result: All public functions automatically get logging, with zero code changes!

This is the vision of Phase 3: Complete separation of concerns through automatic weaving.

Why Annotation-Free Matters

The Problem with Annotations

Even with Phase 1 and Phase 2, annotations are still required:

Phase 1 (Manual):

#![allow(unused)]
fn main() {
#[aspect(Logger)]
fn fetch_user(id: u64) -> User { ... }

#[aspect(Logger)]
fn save_user(user: User) -> Result<()> { ... }

#[aspect(Logger)]
fn delete_user(id: u64) -> Result<()> { ... }

// Must repeat for 100+ functions!
}

Phase 2 (Declarative):

#![allow(unused)]
fn main() {
#[advice(pointcut = "execution(pub fn *(..))", ...)]
fn logger(pjp: ProceedingJoinPoint) { ... }

// Functions still need to be reachable by weaver
// Still some annotation burden
}

Issues:

  • ❌ Code still contains aspect-related annotations
  • ❌ Easy to forget annotations on new functions
  • ❌ Aspects can’t be changed without touching code
  • ❌ Existing codebases require modifications

The Phase 3 Solution

Phase 3 (Automatic):

#![allow(unused)]
fn main() {
// NO annotations whatsoever!
pub fn fetch_user(id: u64) -> User { ... }
pub fn save_user(user: User) -> Result<()> { ... }
pub fn delete_user(id: u64) -> Result<()> { ... }
}

Build configuration:

aspect-rustc-driver --aspect-pointcut "execution(pub fn *(..))" ...

Benefits:

  • ✅ Zero code modifications
  • ✅ Impossible to forget aspects
  • ✅ Change aspects via build config
  • ✅ Works with existing codebases
  • ✅ True separation of concerns

The AspectJ Inspiration

AspectJ pioneered automatic aspect weaving for Java:

AspectJ Code:

// Business logic (no annotations)
public class UserService {
    public User fetchUser(long id) {
        return database.get(id);
    }
}

// Aspect (separate file)
@Aspect
public class LoggingAspect {
    @Pointcut("execution(public * com.example..*(..))")
    public void publicMethods() {}

    @Before("publicMethods()")
    public void logEntry(JoinPoint jp) {
        System.out.println("[ENTRY] " + jp.getSignature());
    }
}

Key Features:

  1. Business logic has no aspect code
  2. Aspects defined separately
  3. Pointcuts select join points automatically
  4. Applied at compile-time or load-time

aspect-rs Phase 3 achieves the same vision in Rust!

What Makes This Hard in Rust

The Compilation Model Challenge

Java/AspectJ approach:

Java Source → Bytecode → AspectJ Weaver → Modified Bytecode

Easy to modify bytecode at any stage.

Rust approach:

Rust Source → HIR → MIR → LLVM IR → Machine Code

Challenges:

  1. No reflection: Rust has no runtime reflection
  2. Compile-time only: All weaving must happen during compilation
  3. Type system: Must preserve Rust’s strict type safety
  4. Ownership: Must respect borrow checker
  5. Zero-cost: Can’t add runtime overhead

The rustc Integration Challenge

To achieve automatic weaving, we need to:

  1. Hook into rustc compilation - Requires unstable APIs
  2. Access type information - Need TyCtxt from compiler
  3. Extract MIR - Analyze mid-level intermediate representation
  4. Match pointcuts - Identify which functions match patterns
  5. Generate code - Weave aspects automatically
  6. Preserve semantics - Maintain exact behavior

This is what Phase 3 accomplishes!

The Breakthrough

What We Achieved

Phase 3 successfully:

  1. Integrates with rustc via custom driver
  2. Accesses TyCtxt using query providers
  3. Extracts function metadata from MIR
  4. Matches pointcut patterns automatically
  5. Generates analysis reports showing what matched
  6. Works with zero annotations in user code

The Technical Solution

Key innovation: Using function pointers with global state

#![allow(unused)]
fn main() {
// Global state for configuration
static CONFIG: Mutex<Option<AspectConfig>> = Mutex::new(None);

// Function pointer (not closure!) for query provider
fn analyze_crate_with_aspects(tcx: TyCtxt<'_>, (): ()) {
    let config = CONFIG.lock().unwrap().clone().unwrap();
    let analyzer = MirAnalyzer::new(tcx, config.verbose);
    let functions = analyzer.extract_all_functions();
    // Apply pointcut matching...
}

// Register with rustc
impl Callbacks for AspectCallbacks {
    fn config(&mut self, config: &mut interface::Config) {
        config.override_queries = Some(|_sess, providers| {
            providers.analysis = analyze_crate_with_aspects;
        });
    }
}
}

This solves the closure capture problem and enables TyCtxt access!

The Impact

Before Phase 3

100 functions needing logging:

#![allow(unused)]
fn main() {
// Must write this 100 times!
#[aspect(Logger)]
fn function_1() { ... }

#[aspect(Logger)]
fn function_2() { ... }

// ... 98 more times ...

#[aspect(Logger)]
fn function_100() { ... }
}

Total: 100 annotations + aspect definition

After Phase 3

100 functions needing logging:

#![allow(unused)]
fn main() {
// Write once - NO annotations!
fn function_1() { ... }
fn function_2() { ... }
// ... 98 more ...
fn function_100() { ... }
}

Build command:

aspect-rustc-driver --aspect-pointcut "execution(*)" main.rs

Total: 1 build command + aspect definition

Reduction: 90%+ less boilerplate!

Real-World Scenarios

Scenario 1: Adding Logging to Existing Codebase

Without Phase 3:

  1. Find all functions that need logging
  2. Add #[aspect(Logger)] to each
  3. Recompile and test
  4. Hope you didn’t miss any

With Phase 3:

  1. Compile with aspect-rustc-driver
  2. Done!

Scenario 2: Performance Monitoring

Without Phase 3:

#![allow(unused)]
fn main() {
#[aspect(Timer)]
fn api_handler_1() { ... }

#[aspect(Timer)]
fn api_handler_2() { ... }

// 50+ handlers to annotate
}

With Phase 3:

aspect-rustc-driver \
    --aspect-pointcut "within(crate::api::handlers)" \
    --aspect-type "TimingAspect"

All handlers automatically monitored!

Scenario 3: Security Auditing

Without Phase 3:

#![allow(unused)]
fn main() {
#[aspect(Auditor)]
fn delete_user() { ... }

#[aspect(Auditor)]
fn modify_permissions() { ... }

#[aspect(Auditor)]
fn access_sensitive_data() { ... }

// Easy to forget on new functions!
}

With Phase 3:

aspect-rustc-driver \
    --aspect-pointcut "execution(pub fn delete_*(..))" \
    --aspect-pointcut "execution(pub fn modify_*(..))" \
    --aspect-type "AuditAspect"

Impossible to forget - automatically applied!

Comparison with Other Languages

FeatureAspectJ (Java)PostSharp (C#)aspect-rs Phase 3
Annotation-free
Compile-time❌ (IL weaving)
Zero overhead
Type-safe❌ (runtime)❌ (runtime)✅ (compile-time)
Pattern matching
Automatic weaving
Rust native

aspect-rs Phase 3 is the first compile-time, zero-overhead, type-safe, automatic AOP framework for Rust!

The Vision Realized

What We Set Out to Do

Create an AOP framework for Rust that:

  • ✅ Matches AspectJ’s automation
  • ✅ Maintains Rust’s type safety
  • ✅ Achieves zero runtime overhead
  • ✅ Works with existing code
  • ✅ Requires no code changes

What We Achieved

Phase 3 delivers:

  • Automatic weaving via rustc integration
  • Zero annotations required in user code
  • Pointcut-based aspect application
  • Compile-time verification and weaving
  • Type-safe through Rust’s type system
  • Zero runtime overhead via compile-time weaving

The Journey

Phase 1 (Weeks 1-4): Basic infrastructure

  • Core trait and macro
  • Manual annotations
  • Proof of concept

Phase 2 (Weeks 5-8): Production features

  • Pointcut expressions
  • Global registry
  • Declarative aspects

Phase 3 (Weeks 9-14): Automatic weaving

  • rustc integration
  • MIR analysis
  • Annotation-free AOP
  • VISION ACHIEVED!

Looking Forward

What’s Possible Now

With Phase 3 complete, we can:

  1. Add logging to entire codebases instantly
  2. Monitor performance across all API endpoints
  3. Audit security operations automatically
  4. Track metrics without code changes
  5. Apply retry logic to flaky operations
  6. Manage transactions declaratively

All with zero code modifications and zero runtime overhead.

Future Enhancements

Phase 3 opens doors for:

  • Field access interception: Intercept field reads/writes
  • Call-site matching: Match at call sites, not just definitions
  • Advanced patterns: More sophisticated pointcut expressions
  • IDE integration: Visual aspect indicators
  • Debugging tools: Aspect-aware debugger

The Promise

Phase 3 delivers on the core promise of AOP:

“Separation of concerns without code pollution”

Your business logic remains pure. Your aspects are defined separately. The compiler weaves them together automatically.

This is the vision of aspect-rs.

See Also