In this lesson, you will learn why function arguments are "contravariant," allowing you to substitute a function that accepts a broader set of inputs for one that expects a narrower set.
Understanding the Direction of Variance
We have previously discussed how lifetimes behave when they are part of a structure or a collection (covariance). However, function arguments follow the exact opposite rule. This is called contravariance.
Think of a function signature as a contract. If a piece of code expects a function that takes a &'a str, it is promising to provide a reference that lives for at least 'a. If you give it a function that can handle any lifetime (an 'static reference, for example), that is perfectly safe. Because the function is capable of handling "more" than what is required, it is a valid substitute.
A Concrete Example
Consider this scenario where we have two functions with different lifetime requirements:
fn needs_long_lived(s: &'static str) { println!("{}", s); }
fn needs_short_lived(s: &'a str) { println!("{}", s); }
If you have a variable that requires a function signature like fn(&'a str), you can safely pass a function that expects fn(&'static str). Why? Because &'static str is a subtype of &'a str (it lives at least as long). Even though the lifetime itself is covariant, the function input position flips this requirement.
If a function promises to work with any &'a str, it will naturally work with the more specific &'static str. Conversely, you cannot replace a function expecting a &'static str with one that only handles &'a str, because the latter might not be able to handle a truly static string if it needs to perform operations restricted by the shorter lifetime.
Why this matters
In Rust, the compiler uses these rules to determine if you can pass a function pointer or a closure into a higher-order function. Because arguments are contravariant, Rust allows you to be more flexible with the lifetimes you provide to function signatures, ensuring the program remains memory-safe while avoiding unnecessary trait bound errors.
Key Takeaway: Function arguments are contravariant, meaning a function that accepts a broad (longer) lifetime can safely be used in place of a function that requires a specific (shorter) lifetime.