0

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);
// }

playground

An overview of other questions about the same error code:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Nickolay
  • 31,095
  • 13
  • 107
  • 185
  • See also [Expected bound lifetime parameter, found concrete lifetime](https://stackoverflow.com/a/32194553/155423); [Function references: expected bound lifetime parameter , found concrete lifetime \[E0271\]](https://stackoverflow.com/q/39395813/155423); [Expected bound lifetime parameter, found concrete lifetime \[E0271\]](https://stackoverflow.com/q/31362206/155423); [Type mismatch “bound lifetime parameter” vs “concrete lifetime” when filling a collection from a closure](https://stackoverflow.com/q/33807396/155423) – Shepmaster May 12 '19 at 02:08
  • It would be good if you took the time to show what research you have done *before* asking the question. One possible way that could have occurred is by reading the numerous questions that have been asked about the exact same error message, linking to them, and explaining why they are different. – Shepmaster May 12 '19 at 02:11
  • @Shepmaster Out of respect for your contributions here, I've added the requested information, though I don't think it was necessary. I wonder what prompted you to make such a comment, though, as it felt unnecesarily harsh. Did this look like a question asked without doing any research? Did you mean that there was another question that answers or at least hints at an answer? (And why remove the formatting, did it make the question harder to read?) – Nickolay May 12 '19 at 17:42
  • 1
    `run_method(|foo| foo.run());` - that's it – Laney May 13 '19 at 03:56
  • @Laney that works, thanks! Still unsure if there's anything wrong with passing the method directly ([as clippy insists I do](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure)). – Nickolay May 13 '19 at 13:06

1 Answers1

1

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.

Isaac van Bakel
  • 1,772
  • 10
  • 22
  • Thanks for the `Vec<&'static i32>` pointer! That part makes sense now. I still don't get the "the type of run is constrained by the type of Foo" bit - specifically what's the practical difference between a proper method that's constrained by the lifetime parameter from `impl<'a>` and the not-a-method `fn run<'s>(_self: &Foo<'s>)`, but that gives me something to think about. Would it be wrong to say that the language rules could be changed to allow `run_method(Foo::run)` to compile without causing issues at runtime, and the "run is constrained by the type of Foo" is a design decision? – Nickolay May 14 '19 at 21:35
  • The difference is that Foo is a type with some lifetime. `Foo` is only a shorthand for `Foo<'a>` where the compiler can infer `'a` - and the type signature of `run` is dependent on that `'a`, because it uses the `Self` type in the `impl`. I don't know if the language could be changed without breaking something - nothing immediately comes to mind, but because the `impl` has a fixed lifetime, it could always do something unexpected. – Isaac van Bakel May 15 '19 at 10:36