A function that returns its borrowed input can leave the lifetime names out:
fn identity(x: &i32) -> &i32 {
x
}
fn main() {
let number = 7;
assert_eq!(*identity(&number), 7);
}
The signature means fn identity<'a>(x: &'a i32) -> &'a i32: each call
accepts a reference and returns one valid for the same lifetime. The caller
does not have to supply a reference that lives forever.
A similar-looking closure can fail. This example intentionally does not compile:
fn main() {
let identity = |x: &i32| x;
let number = 7;
assert_eq!(*identity(&number), 7);
}
The diagnostic says that a lifetime may not live long enough. The standalone
closure annotation has not established the input/output relationship needed
here. Adding -> &i32 to this closure is not enough to resolve it.
This is an inference issue, not evidence that returning a borrowed argument is unsafe. An expected callable signature can give inference the relationship it needs. Elision rules apply to function and callable trait signatures; that does not mean every locally annotated closure expression gets the same inferred signature automatically.
Use a function pointer when nothing is captured
A noncapturing closure can coerce to a function pointer. Annotating the receiving binding supplies a signature with linked input and output lifetimes:
fn main() {
let identity: fn(&i32) -> &i32 = |x| x;
let outer = 7;
assert_eq!(*identity(&outer), 7);
{
let inner = 9;
assert_eq!(*identity(&inner), 9);
}
assert_eq!(*identity(&outer), 7);
}
The pointer type can also be written
for<'a> fn(&'a i32) -> &'a i32. The for<'a> says that it works for each
input lifetime, not one fixed lifetime chosen when the pointer was created.
A named function such as the first example is often the clearest choice
when there is no environment to capture.
Supply a bound when the closure captures data
A capturing closure cannot coerce to a function pointer. A generic helper can instead describe the required relationship with a higher-ranked trait bound. This helper returns the same closure value it receives:
use std::cell::Cell;
fn linked<F>(f: F) -> F
where
F: for<'a> Fn(&'a i32) -> &'a i32,
{
f
}
fn main() {
let calls = Cell::new(0);
let identity = linked(|x| {
calls.set(calls.get() + 1);
x
});
let outer = 7;
assert_eq!(*identity(&outer), 7);
{
let inner = 9;
assert_eq!(*identity(&inner), 9);
}
assert_eq!(calls.get(), 2);
}
The bound relates each returned reference to that call's input. It does not
require the captured calls value to be 'static. The closure borrows the
counter and cannot outlive it. Cell permits updating the counter through
a shared reference, so this closure still implements Fn.
Put the bound where the closure is created, as in linked(|x| ...).
A closure already inferred in another context may not satisfy a more general
bound later. Adding a helper after that point is not a universal repair.
Context matters more than whether there is a binding
A stored closure can return a reference successfully. For example, an
unannotated let identity = |x| x; can infer an argument type from a local
call. That does not prove it works for every reference lifetime required by
a higher-ranked API. Conversely, directly calling an annotated closure can
still produce the lifetime error; avoiding a binding is not a general fix.
Iterator adapters provide another useful context. In this example, each
closure input is a reference to a particular owned String, and the result
borrows from that same string:
fn main() {
let names = [String::from("ada"), String::from("bo")];
let borrowed: Vec<&str> = names.iter().map(|name| name.as_str()).collect();
assert_eq!(borrowed, ["ada", "bo"]);
}
Keep the owner alive while the collected references are used. Creating a temporary vector, collecting references into it and using those references after the statement ends fails for a separate reason: the temporary owner has already been dropped. A callable signature cannot extend its lifetime.
Choosing a repair
First identify what the returned reference borrows from. If it borrows from an argument, describe that relationship in a function signature, function pointer type or callable bound. If it borrows from captured data, the capture's lifetime also matters. If the owner is already gone, fix the ownership scope.
Type inference uses expected types, annotations and uses throughout the
surrounding context. It is not simply a rule that the first call determines
everything. Still, an ordinary identity closure does not become generic over
unrelated types: one closure used with both an integer and a String gives
a type mismatch. Use a generic function when that is the API you need.