问题
I'm getting an error when trying to use a closure that does exactly the same as the print
function below (in ln.9)
The error is the usual borrowed value does not live long enough
. I've tried to replicate this in the playground
but I can't. I'm certain that this is mainly because I don't really understand what's going on here so any help would be really appreciated.
What I can't understand is what is the difference between calling the print
function and calling the check
closure. They have exactly the same signature and even the same body.
How does the context in which they were created affect the borrow checker? What would be the solution to this?
extern crate typed_arena;
use typed_arena::Arena;
#[derive(Debug)]
struct AstNode<'a> {
name: &'a str,
}
fn get_ast<'a>(path: &str, arena: &'a Arena<AstNode<'a>>) -> &'a AstNode<'a> {
// ...
}
type CheckFn<'a> = dyn Fn(&'a AstNode<'a>);
fn print<'a>(root: &'a AstNode<'a>) {
println!("{:?}", root);
}
fn it_does_not_have_details_if_all_ok<'a>(file: &str, check: Box<CheckFn<'a>>) {
let arena = Arena::new();
let a = &arena;
let root = get_ast(file, a);
println!("{:?}", root);
// Works
print(root);
// Produces an error
check(root);
}
The error is:
error[E0597]: `arena` does not live long enough
--> src/main.rs:21:14
|
21 | let a = &arena;
| ^^^^^ borrowed value does not live long enough
...
28 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 19:1...
--> src/main.rs:19:1
|
19 | fn it_does_not_have_details_if_all_ok<'a>(file: &str, check: Box<CheckFn<'a>>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
回答1:
They have exactly the same signature and even the same body.
The body is not relevant because the type checker treats functions as a black box, and only looks at types. But, while the signatures may look the same, they are not. The difference is how the lifetime parameter is bound.
The lifetime parameter 'a
of print<'a>
is bound at the point that you call it. Since you are passing root
as the argument, and root
is a reference, you are implicitly instantiating 'a
to be the lifetime of that reference. This is exactly what you want because root
lives longer than the call to print
.
But the lifetime parameter 'a
of check<'a>
is bound before you call it. Instead, you have bound it to the lifetime parameter of the function it_does_not_have_details_if_all_ok<'a>
, which is determined by the caller of it_does_not_have_details_if_all_ok
, so could be any lifetime that is longer than this function call. This is definitely not what you want because:
- The reference
root
does not live that long (because it holds a reference toarena
which is local to the function). - The function
check
does not even need its argument to live that long.
This is exactly the same as the reason why you can't return a reference to a variable created in a function. The difference is that you don't actually even need this lifetime constraint.
I can't easily test this out because you only posted an image of your code, and you have a few definitions not provided. But the quick fix is to use a higher-ranked trait bound (HRTB) on CheckFn
:
type CheckFn = dyn for<'a> Fn(&'a AstNode<'a>);
This gets rid of the need to bind 'a
whenever you mention CheckFn
. Instead, the lifetimes are bound at the point when the inner function is called, just like it is for print
.
As pointed out in the comments, you can elide these lifetimes altogether:
type CheckFn = dyn Fn(&AstNode);
This will cause the type checker to infer the lifetimes slightly more generally than above:
type CheckFn = dyn for<'a, 'b> Fn(&'a AstNode<'b>);
来源:https://stackoverflow.com/questions/51969236/lifetime-issues-with-a-closure-argument-in-rust