I have some code that I've simplified to the following, which would fail at runtime, but doesn't compile due to lifetime issues. My actual code, of course, does not feature infinite recursion.
fn demo_clap<'a,'b,T>(name: &'a str, app: clap::App<'a,'b'>,
f: impl FnOnce(clap::App<'a,'b>) -> T)
-> T {
let foo = "hello".to_string();
demo_clap(&foo, app, f)
}
I know that foo
lives long enough, since the whole app
is consumed before the function exits. Although now that I think about it, perhaps the compiler is assuming that T
may contain a reference?
Is there any way to constrain a generic type to not have a reference in it?
T: 'static
(this permits static refs in T but that’s fine for your purposes).
1 Like
I don't think T
is your lifetime problem. The signature fn demo_clap<'a, 'b, T>
means that the caller gets to pick 'a
, 'b
, and T
. Suppose that in the outermost call to demo_clap
the caller picks 'a = 'static
. Then f
implements FnOnce(App<'static, 'b>) -> T
. The nested call attempts to pass f
, which implements FnOnce(App<'static, 'b>) -> T
, and &foo
, which is a &str
but definitely not &'static str
. Let's give it a name and call it &'tmp str
. So what is 'a
in the nested call to demo_clap
? It can't be 'a = 'static
because the first argument does not live that long. It can't be 'a = 'tmp
because f
does not necessarily implement FnOnce(App<'tmp, 'b>) -> T
.
The error says the same thing:
error[E0597]: `foo` does not live long enough
|
| demo_clap(&foo, app, f)
| ^^^ borrowed value does not live long enough
| }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a
2 Likes
It does seem that removing the lifetimes in the type declaration of f
fixes the problem! I also added a static constraint on T for good measure. Thanks!
Yeah, @dtolnay was on the money. Removing the lifetimes makes it a HRTB, which is what you want; essentially F: for<'x, 'y> FnOnce(App<'x, 'y) -> T
if you wanted to be explicit.
1 Like