Reputation: 32073
In my tests I had a helper function that runs a given method on differently configured objects, with a simplified version looking like this:
fn run_method<F>(f: F)
where
F: Fn(&Foo),
{
let to_test = vec![0i32];
to_test
.iter()
.map(|param| {
let foo = Foo(*param);
f(&foo);
})
.for_each(drop);
}
// run_method(Foo::run);
This worked fine until I added references to the tested struct, making it "lifetime-annotated" (for lack of a proper term, I mean Foo<'a>
).
Now I get an error indicating, I think, that Rust doesn't want to accept a Foo::method
as a function that can be used with any given lifetime (i.e. F: for<'a> Fn(&Foo<'a>)
), as required by the closure:
error[E0631]: type mismatch in function arguments
--> src/main.rs:54:5
|
3 | fn run(&self) {
| ------------- found signature of `for<'r> fn(&'r Foo<'_>) -> _`
...
54 | run_method(Foo::run);
| ^^^^^^^^^^ expected signature of `for<'r, 's> fn(&'r Foo<'s>) -> _`
|
note: required by `run_method`
--> src/main.rs:44:1
|
44 | fn run_method<F>(f: F) where F: Fn(&Foo) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0271]: type mismatch resolving `for<'r, 's> <for<'t0> fn(&'t0 Foo<'_>) {Foo::<'_>::run} as std::ops::FnOnce<(&'r Foo<'s>,)>>::Output == ()`
--> src/main.rs:54:5
|
54 | run_method(Foo::run);
| ^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
|
note: required by `run_method`
--> src/main.rs:44:1
|
44 | fn run_method<F>(f: F) where F: Fn(&Foo) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I can work around the problem by avoiding closures (though I don't really understand how 'a
gets constrained to be local to run_method
- isn't the lifetime parameter supposed to be chosen by the caller?):
fn run_method<'a, F>(f: F)
where
F: Fn(&Foo<'a>),
{
let to_test = vec![&0i32];
for param in to_test {
let foo = Foo(param);
f(&foo);
}
}
Can I fix this without rewriting? If not - is there a reason why this shouldn't work?
Complete code:
struct Foo<'a>(&'a i32);
impl<'a> Foo<'a> {
fn run(&self) {
println!("Hello {}", self.0);
}
}
fn run_method<F>(f: F)
where
F: Fn(&Foo),
{
//same as F: for<'a> Fn(&Foo<'a>) {
let to_test = vec![0i32];
to_test
.iter()
.map(|param| {
let foo = Foo(param);
f(&foo);
})
.for_each(drop);
}
fn main() {
run_method(Foo::run);
}
// This works:
// fn run_method<'a, F>(f: F)
// where
// F: Fn(&Foo<'a>),
// {
// let to_test = vec![&0i32];
// for param in to_test {
// let foo = Foo(param);
// f(&foo);
// }
// }
// The lifetime-less version:
// struct Foo(i32);
// impl Foo {
// fn run(&self) {
// println!("Hello {}", self.0);
// }
// }
//
// fn run_parser_method<F>(f: F)
// where
// F: Fn(&Foo),
// {
// let to_test = vec![0i32];
// to_test
// .iter()
// .map(|param| {
// let foo = Foo(*param);
// f(&foo);
// })
// .for_each(drop);
// }
//
// fn main() {
// run_parser_method(Foo::run);
// }
An overview of other questions about the same error code:
trait { fn handle<'a>(); }
vs impl<'a> { fn handle() {} })
|args| {...}
without explicit type annotations (|args: &[&str]|
) not being accepted as a Fn(&[&str]) -> ()
; the answers don't explain why (the latter hints that it was not implemented in 2015)let mut insert = |k| seq.insert(k); (1..10).cycle().take_while(insert)
), which masks a more useful "borrowed data cannot be stored outside of its closure" error.Upvotes: 0
Views: 653
Reputation: 1862
I can work around the problem by avoiding closures (though I don't really understand how 'a gets constrained to be local to run_method - isn't the lifetime parameter supposed to be chosen by the caller?)
It is. But when you rewrite it without closures, you have also put the reference inside the vec!
invocation, so it is no longer constructed at runtime. Instead, the compiler can infer that to_test
has type Vec<&'static i32>
, and as 'static
outlives any caller-chosen lifetime, there's no violation.
This fails to compile as you expect:
let to_test = vec![0i32];
for param in to_test.iter() {
let foo = Foo(param);
f(&foo);
}
is there a reason why this shouldn't work?
Yes, because the type of run
is constrained by the type of Foo
. More specifically, you have a lifetime decided by the caller (implicitly, in the type for Foo
), so you have to construct a Foo
of that lifetime to invoke the given run
reference on it.
Can I fix this without rewriting?
That depends.
If all your test values are literals, you can make 'static
references.
If you're able and willing to rewrite run
, it's possible to make it unconstrained by the type of Foo
impl<'a> Foo<'a> {
fn run<'s>(_self: &Foo<'s>) {
println!("Hello {}", _self.0);
}
}
But then it doesn't need to go in an impl
block at all.
I think the solution provided in the comments is your best bet, because given that you don't care about your concrete Foo<'a>
type, there's no need to give the method reference for that type.
Upvotes: 1