Course contentsShow
← Rust
Lesson 414 of 1,9104. Lifetimes and Advanced OwnershipFree lesson

Context-Passing Architecture

Pass operational contexts through functions using lifetimes to track authority.

In this lesson, you will learn how to use lifetime-bound "Context" objects to thread authority and state through your function call tree without relying on global variables.

The Problem with Globals

In large systems, functions often need access to shared resources like database connections, configuration settings, or logging handles. Passing these as individual arguments creates "argument soup," while global variables create hidden dependencies that make your code fragile and difficult to test. Context-Passing Architecture solves this by bundling these requirements into a single, immutable Context struct that is threaded through your execution flow.

Tying Authority to Lifetimes

When a context holds references to external resources (like an Arena or a scoped configuration), those references must be tied to the lifetime of the call stack. By using a lifetime parameter 'ctx, we ensure that the context cannot outlive the resources it governs.

Consider a system where a Context holds a reference to a configuration object:

struct AppContext<'ctx> {
    config: &'ctx Config,
    log_level: &'static str,
}

fn process_data<'ctx>(ctx: &AppContext<'ctx>, data: &str) {
    if ctx.config.verbose {
        println!("[{}] Processing: {}", ctx.log_level, data);
    }
    // Deeply nested calls still have access to the context
    step_two(ctx, data);
}

By defining the function as process_data<'ctx>, you guarantee at compile time that the AppContext being passed in is valid for the duration of the entire operation. If the Config object is dropped, the compiler will prevent you from invoking any function that requires the AppContext.

Why this matters

This approach creates a clear, explicit "chain of custody" for your application's state. Because the lifetime of the Context is bound to the resources it encapsulates, you gain "transactional safety"—you know exactly what data is accessible at any given point in your program's execution. It turns "magic" global access into a predictable, type-safe flow of information that the borrow checker tracks for you.

Key Takeaway: Context-Passing Architecture uses lifetime-bound structures to explicitly thread dependencies through your call stack, ensuring safe, compile-time validation of resource access.