In this lesson, you will learn how to weave multiple lifetime constraints together to create precise, complex dependency graphs between borrowed references.
The Problem: When One Lifetime Isn't Enough
Often, a structure doesn't just borrow one thing; it borrows several, and those borrowed items might have relationships with each other. For example, imagine a Parser that holds a reference to a Source string and a Logger that also references the Source. If the Parser needs to store a reference to the Logger, you suddenly have a web of constraints.
"Composable borrowing" means expressing these relationships by using multiple lifetime parameters and defining how they relate to one another, rather than forcing everything into a single, restrictive lifetime.
Expressing Dependency Graphs
When you have multiple inputs, you can relate their lifetimes using lifetime bounds. This allows you to say, "The reference B must live at least as long as reference A."
Consider a simple scenario where a Context holds two references:
struct Context<'a, 'b> {
data: &'a str,
// The logger must be valid for at least as long as 'b,
// and 'b must be at least as long as 'a.
logger: &'b Logger<'a>,
}
By using 'a and 'b separately, we avoid "lifetime pollution." If we forced everything to use just 'a, the compiler would assume the Logger and the data must have the exact same lifetime. By composing them, we tell the compiler exactly how the dependencies flow: the Logger depends on the data, and our Context depends on both.
Why Compose?
Composing constraints keeps your API flexible. It prevents the "greedy lifetime" problem where one short-lived reference accidentally shrinks the lifetime of your entire struct to the smallest possible window. Instead, by labeling each source clearly, you allow the compiler to prove that as long as the underlying dependencies are satisfied, your complex object remains valid.
You are effectively drawing a map for the borrow checker, showing it which parts of your data rely on which specific origins.
Key Takeaway: Composable borrowing allows you to model multi-input dependencies by defining distinct lifetimes for each reference and using trait or lifetime bounds to express the hierarchical relationship between them.