shawn
December 3, 2018, 10:58am
1
struct My(i32);
fn call_with_ref<F>(some_closure: F) -> i32
where
F: for<'a> Fn(&'a My) -> &'a i32,
{
let value = My(0);
*some_closure(&value)
}
fn main() {
let _f = |arg: &My| &arg.0;
//This doesn't work
call_with_ref(_f);
//This is ok.
//call_with_ref(|arg: &My| &arg.0);
}
(Playground )
Errors:
Compiling playground v0.0.1 (/playground)
error[E0271]: type mismatch resolving `for<'a> <[closure@src/main.rs:14:14: 14:31] as std::ops::FnOnce<(&'a My,)>>::Output == &'a i32`
--> src/main.rs:16:5
|
16 | call_with_ref(_f);
| ^^^^^^^^^^^^^ expected bound lifetime parameter 'a, found concrete lifetime
|
note: required by `call_with_ref`
--> src/main.rs:3:1
|
3 | / fn call_with_ref<F>(some_closure: F) -> i32
4 | | where
5 | | F: for<'a> Fn(&'a My) -> &'a i32,
6 | | {
7 | | let value = My(0);
8 | | *some_closure(&value)
9 | | }
| |_^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0271`.
error: Could not compile `playground`.
To learn more, run the command again with --verbose.
Why directly passing the closure is ok but not with a variable binding?
shawn
December 3, 2018, 11:42am
2
Seems it happens to be a known issue:
opened 05:40AM - 05 Apr 17 UTC
A-typesystem
C-enhancement
A-diagnostics
P-medium
A-closures
T-compiler
A-inference
D-confusing
D-newcomer-roadblock
This [example](https://play.rust-lang.org/?gist=e1edb587259e492444d69d63f2052537… &version=nightly&backtrace=0):
```rust
pub struct Request<'a, 'b: 'a> {
a: &'a (),
b: &'b (),
}
pub trait Handler: Send + Sync + 'static {
fn handle(&self, &mut Request) -> Result<(),()>;
}
impl<F> Handler for F where F: Send + Sync + 'static + Fn(&mut Request) -> Result<(),()> {
fn handle(&self, _: &mut Request) -> Result<(),()> {
unimplemented!()
}
}
fn make_handler(h: &'static Handler) -> Box<Handler> {
Box::new(move |req| h.handle(req))
}
```
errors with
```
error[E0271]: type mismatch resolving `for<'r, 'r, 'r> <[closure@<anon>:14:14: 14:38 h:_] as std::ops::FnOnce<(&'r mut Request<'r, 'r>,)>>::Output == std::result::Result<(), ()>`
--> <anon>:14:5
|
14 | Box::new(move |req| h.handle(req))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter , found concrete lifetime
|
= note: concrete lifetime that was found is lifetime '_#9r
= note: required because of the requirements on the impl of `Handler` for `[closure@<anon>:14:14: 14:38 h:_]`
= note: required for the cast to the object type `Handler`
```
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?).
1 Like
I’ve seen this type of thing before. One other interesting bit here is coercing the closure to fn(&My) -> &i32
, and using that for the call to call_with_ref
, makes lifetime region inference (I believe that’s the right term) work.
ExpHP
December 4, 2018, 7:08pm
4
It's more subtle than that even. Merely assigning it to a local makes it too late to even coerce it anymore.
// works
let f: fn(&My) -> &i32 = |arg: &My| &arg.0;
// fails. wtf?
let f = |arg: &My| &arg.0;
let f: fn(&My) -> &i32 = f;
Yup. As you rightfully note in your comments, it's a true "wtf" . There are other wtf's in Rust, and it'd be great to resolve them before everyone focuses on newer shinier things (that's essentially the thrust behind this post).
1 Like