Errors like this can happen when you have a closure that is inferred to be tied to a specific lifetime instead of higher-ranked over a generic lifetime. It's a known short-coming of the current implementation, and yes, the error is confusing. The "more general than the other" is a hint that higher-ranked types are involved.
You can fix it by "forcing" the higher-ranked type -- by putting the closure in a context where the compiler expects a higher-ranked Fn
implementation.
fn func_hrtb<F: Fn(&str) -> Result<&str, &str>>(f: F) -> F { f }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// "Be higher-ranked (generic) over your input lifetime"
// Aside from enforcing that bound, this is just an identity function
// Elsewhere...
let combined = func_hrtb(move |s: &str| { /* ... */ });
Playground... but wait. Now we have an even bigger mess of errors! This time it's about a conflict between bounds ("cannot infer an appropriate lifetime for autoref due to conflicting requirements"), but a similar "Expected X, found X" note is still there. Now what?
Well, there's another error about the run
method, which was also present the first time. Let's take a look at that:
fn run(&self, s1: &str) -> Result<&str, &str> {
(self.clone().f)(s1)
}
Based on the lifetime elision rules, this function signature says:
fn run<'s, 'a>(&'s self, s1: &'a str) -> Result<&'s str, &'s str> {
"Borrow me (self
) for 's
and I'll give you an 's
-limited Result
-- no matter the lifetime of s1
!"
But looking at the implementation, you clone
self (making the lifetime of the borrow of self
irrelevant), and then call f
with s1
. And f
has the signature:
Arc<dyn Fn(&str) -> Result<&str, &str>>
// i.e., implements...
Fn(&str) -> Result<&str, &str>
// i.e., via elision rules again
for<'any> Fn(&'any) -> Result<&'any, &'any>
Which is to say that elision gave pretty much the opposite of what we implemented -- the lifetime on self
should be ignored, and the lifetime on s1
should be propagated to the Result
. That is:
fn run<'s>(&self, s1: &'s str) -> Result<&'s str, &'s str> {
(self.clone().f)(s1)
}
With that change, the example compiles. The mess of errors we got in the intermediate step were there because they were based on the signature of run
(and not its implementation). The signature is the contract; when evaluating and_then
, the compiler assumed run
would do what the signature said it would. And that created a conflict in lifetime bounds that it couldn't resolve.
If you start from the OP and apply the changes in the opposite order, you don't get an intermediate mess, you just remove one error and then the other. I don't have any insight on how to choose which errors to address first in a general sense, though.