-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Confusing type error due to strange inferred type for a closure argument #41078
Comments
This is a known issue IIRC, but I don't know how to actually find the original issue. |
Sorry, I've been putting off this tab for days trying to squeeze out some time to write a more detailed comment. But in short the problem is that we do not infer when to "generalize" the region -- in particular, we will infer a type of |
Just ran into this (or at least I'm pretty sure it's this, otherwise feel free to split this off into a new bug) Smaller test case: fn main() {
let suitable = |_| true;
vec![1,2,3].into_iter().find(suitable);
} Workarounds: Inline suitable into find, or annotate suitable with a I think all of these issues are older duplicates
|
More duplicates, or at least the same underlying issue:
We should decide on a single (or two, one for the diagnostic and one for the underlying issue) canonical issue and deduplicate. |
Leaving the following issues open, since I don't think they're quite duplicates. The rest of the issues linked in the two comments above are about to be closed referencing this issue. |
I wanted to re-include and extend @shepmaster's example from #26937 as it includes a potential work-around (?). Namely that including the closure directly into the call of the function vs. binding it avoids the problem entirely and using type annotations can sometimes avoid the problem. Hopefully this is insightful and those more experienced can weigh in more. fn caller<F>(f: F) where F: Fn(&i32) {
f(&42);
}
fn caller_ref_return<F>(f: F) where F: Fn(&i32) -> &i32 {
f(&42);
}
fn main() {
// Works
caller(|a| println!("{}", a));
// Works
let f = |a: &i32| println!("{}", a);
caller(f);
// Doesn't work (error: type mismatch)
let f = |a| println!("{}", a);
caller(f);
// Works
caller_ref_return(|a| a);
// Doesn't work (error: expected bound lifetime parameter, found concrete lifetime)
let f = |a: &i32| a;
caller_ref_return(f);
// Doesn't work (error: type mismatch)
let f = |a| a;
caller_ref_return(f);
} |
Not sure if anyone else pointed this out, but wanted to specifically highlight this part of the error message:
This is unreadable − all the lifetime parameters are assigned the same name! |
Another instance of this was just brought up on let s = "hello";
let re = regex::Regex::new("[aeiou]").unwrap();
let after = re.replace_all(s, |c| "xxx".to_string());
println!("{} -> {}", s, after); fails with the supremely unhelpful error:
Compiles fine if you use |
I'm dumb, but isn't this a lot like the monomorphization restriction in Haskell, but with lifetimes instead of types? |
Regarding this example that @jonhoo cited (playground link), the cause is that, because the signature for I note this because, in addition to improving the error msg, I'd of course prefer if that example just worked -- but resolving that issue (figuring out bound regions more generally) is a bit of an open question. In principle it may be possible, I haven't thought hard about it, but it'll require a lot of refactoring before we get there. Anyway, we ought to be able to improve the error message in the meantime I suppose. |
(I suppose that we might, in this particular case, be able to get smarter about the expected type. There is even an open issue on this, but I can't find it just now.) |
|
Maybe similar issue playground fn main() {
let s = "";
((&|_| s) as &dyn Fn(&str) -> &str)(s);
} But this works fn main() {
let s = "";
let f: &dyn Fn(&str) -> &str = &|_| s;
f(s);
} And another one |
Assigning Additionally we nominate this longstanding issue to be discussed during the next meeting |
Triage: Current output:
|
Related: #91966 |
I don't think any of the posts here mention a workaround (until https://rust-lang.github.io/rfcs/3216-closure-lifetime-binder.html gets implemented). I have been able to use the following workaround (shown for the example posted by @estebank).
While in the above example, the closure can be passed to
I had three questions about this workaround:
Disclaimer: I am fairly new to rust. |
@108anup there's no way of being explicit yet in stable Rust, but nightly Rust supports specifying the |
@108anup: Yes to 1. and 2.: We can't properly give a name to the bound AFAIK since trait alias are unstable, but we can make a coercion function with a user-chosen name which is basically as good in this case. Then the user can just reuse that coercion function without having to repeat the details. I hope the |
@douglas-raillard-arm Thanks, this is very helpful!! I wanted to give a name to the trait bound so that I can use the name in (1) the coercion function (when creating the closure) as well as (2) the function (or struct) that consumes the closure. E.g., in your playground link, the function signature is repeated on line 34. Is there a way to avoid repeating the definition (function signature). My current workaround has been on the following lines (i.e., store reference to trait object). I think there were some cases of closures I wan't able to represent this way. I don't recall exactly what though.
|
In unstable Rust you can with trait_alias feature: |
This example:
errors with
Annotating the closure parameter
|req: &mut Response|
allow the example to compile.Interesting, annotating with
|req: &&mut Response|
produces a similarly-structured error, so I believe we're inferring&&mut
here (maybe related to autoderef?).The text was updated successfully, but these errors were encountered: