rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.01k stars 12.54k forks source link

Confusing type error due to strange inferred type for a closure argument #41078

Open jseyfried opened 7 years ago

jseyfried commented 7 years ago

This example:

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?).

jseyfried commented 7 years ago

cc @nikomatsakis @eddyb

eddyb commented 7 years ago

This is a known issue IIRC, but I don't know how to actually find the original issue.

nikomatsakis commented 7 years ago

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 &'a mut Handler for some 'a, but your type demands a type of &'a mut Handler for all 'a (i.e., the difference between fn(&'a mut Handler) and for<'a> fn (&'a mut Handler). We do not (yet) infer generalizations except when the expected type provides the hint -- now, in this case, I might expect us to be able to trace things through the expected type. We would I think do so if you were returning Box<FnMut()>, but because the FnMut() requirement is "hidden" (this is a trait that happens to be implemented by closures...) we fail to deduce it. I'm pretty sure there's an open issue on this somewhere.

gmorenz commented 7 years ago

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 & type, i.e. let suitable = |_: &_| true.

I think all of these issues are older duplicates

Mark-Simulacrum commented 7 years ago

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.

Mark-Simulacrum commented 7 years ago

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.

rminderhoud commented 7 years ago

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.

https://is.gd/6jjGgX

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);
}
Rufflewind commented 7 years ago

Not sure if anyone else pointed this out, but wanted to specifically highlight this part of the error message:

for<'r, 'r, 'r>

This is unreadable − all the lifetime parameters are assigned the same name!

jonhoo commented 6 years ago

Another instance of this was just brought up on #rust-beginners with the regexp crate:

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:

error[E0271]: type mismatch resolving `for<'r, 's> <[closure@src/main.rs:7:35: 7:56] as std::ops::FnOnce<(&'r regex::Captures<'s>,)>>::Output == std::string::String`
 --> src/main.rs:7:20
  |
7 |     let after = re.replace_all(s, |c| "xxx".to_string());
  |                    ^^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
  |
  = note: required because of the requirements on the impl of `regex::Replacer` for `[closure@src/main.rs:7:35: 7:56]`

error[E0631]: type mismatch in closure arguments
 --> src/main.rs:7:20
  |
7 |     let after = re.replace_all(s, |c| "xxx".to_string());
  |                    ^^^^^^^^^^^    --------------------- found signature of `fn(_) -> _`
  |                    |
  |                    expected signature of `for<'r, 's> fn(&'r regex::Captures<'s>) -> _`
  |
  = note: required because of the requirements on the impl of `regex::Replacer` for `[closure@src/main.rs:7:35: 7:56]`

error: aborting due to 2 previous errors

Compiles fine if you use |c: &regex::Captures|.

quadrupleslap commented 6 years ago

I'm dumb, but isn't this a lot like the monomorphization restriction in Haskell, but with lifetimes instead of types?

nikomatsakis commented 6 years ago

Regarding this example that @jonhoo cited (playground link), the cause is that, because the signature for replace_all requires only that R: Replacer, rather than say R: FnMut(&Captures) -> String, we don't have the "expected type" for the closure's arguments at the time that we need them. As a result, we fail to infer that the region must be bound in the closure (sorry, jargon).

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.

nikomatsakis commented 6 years ago

(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.)

estebank commented 5 years ago

Another repro case:

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);
}
Wizr commented 5 years ago

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

amoffat commented 4 years ago

I'm new to Rust, and this had me pulling my hair out for awhile. I still don't know why the "solution" works.

struct S {}

fn filter<P>(predicate: P)
where
    P: Fn(&S) -> bool,
{
    predicate(&S{});
}

fn main() {
    // this works
    filter(|_s| true);

    // this also works
    fn cb1(_s: &S) -> bool {
        true
    }
    filter(cb1);

    // but this doesn't work
    let cb2 = |_s| true;
    filter(cb2);
}
eddyb commented 4 years ago

@amoffat Generally you should avoid putting closures in variables, if you're only going to pass them to a function, because placing the closure expression nested in the call expression lets the compiler deduce a better signature.

Why the signature can even differ is that your P: Fn(&S) -> bool bound is really sugar for this bound: P: for<'a> Fn(&'a S) -> bool. When you write filter(|_s| true), the compiler can pick that for<'a> for the closure signature as well, and everything works.

(the use of for<...> here is the mathematical ∀, aka "forall", aka "universal quantification" - for<'a> means "for all possible lifetimes 'a", and that's very different in power, from inferring a specific lifetime)

Your fn cb1(_s: &S) -> bool is also sugar, for fn cb1<'a>(_s: &'a S) -> bool, so you get that same for<'a> aspect ahead of the filter(cb1).

With cb2, however, the closure starts out with no for<...> lifetimes, and that aspect can't be inferred back from filter(cb2).

If rustc were smarter, it could wait to see a use of the closure, before type-checking its body, which would also help a few other cases, but it's tricky. Once you have a closure in a variable it can be used multiple times, and those use might have conflicting needs.

amoffat commented 4 years ago

@eddyb, I still don't understand :) The fix from the SO answer was the following:

fn filter<'a, P>(predicate: P)
where
    P: Fn(&'a S) -> bool,
{
    predicate(&S {});
}

Based on what you said, the code now works because I've narrowed the lifetime from the "forall" to a specific lifetime? I'm still unclear on the logic of why a lifetime is needed at all. Is it because the borrow checker needs to make sure that the predicate is only called on a valid borrow of S? That doesn't make sense because it can only be called with a valid borrow of S.... so there is nothing to check.

eddyb commented 4 years ago

@amoffat Ah, my bad, I didn't click through to the SO post.

&T never exists without a lifetime (i.e. it's always sugar when you don't see a lifetime explicitly written), and the lifetime is used to keep track of all the other references derived from that reference - which in your case is none, but that's only a coincidence, as far as the compiler is concerned.

For example, with the P: Fn(&S) -> bool form, the closure doesn't know how long it can keep that reference around, whereas with P: Fn(&'a S) -> bool from your last comment, the caller of filter can pick the 'a and, say, stash the reference from the closure to some outer scope.

The only reason predicate(&S {}); even compiles in your last comment is another language feature, which makes references to constant expressions have 'static lifetime.

If you wrote let s = S {}; predicate(&s); that wouldn't work, because 'a can be whatever the caller of filter wants it to be (including 'static), and s doesn't survive past filter returning.

amoffat commented 4 years ago

The only reason predicate(&S {}); even compiles in your last comment is another language feature, which makes references to constant expressions have 'static lifetime.

If you wrote let s = S {}; predicate(&s); that wouldn't work, because 'a can be whatever the caller of filter wants it to be (including 'static), and s doesn't survive past filter returning.

@eddyb Ah, I didn't catch that from my example! I think that is making the picture more clear for me.

But I'm still unclear on why the compiler cares about the lifetime of &'a S reference in the closure. Are you saying it is because of the possibility that the closure can copy the reference into one of its captured fields? And because of this possibility, the borrow checker needs a guarantee ('a) that any input references to the closure will outlive the closure?

I know that closures are represented internally as unique structs, so this is starting to make sense. If a closure's capture field can be assigned a reference to an input parameter, then I can see why the compiler cares that the lifetime of the input parameters outlive the closure itself. The fact that I am not actually capturing anything should allow the compiler to relax its restrictions a bit, but currently it is just being unnecessarily strict. Am I on the right track?

eddyb commented 4 years ago

@amoffat You're right that you could do that with a capture, but you don't even need captures, you could be trying to store a &'static S into a thread_local! variable, for example.

And little of that is understood by the compiler until borrow-checking runs, and borrow-checking enforces the rules that arise from the inferred types, so information can't flow back into inference.

amoffat commented 4 years ago

That was helpful @eddyb , thanks for explaining

Jezza commented 4 years ago

I just got bitten by this.
I'm implementing a trait on a Closure:

impl MyTrait for for<'a> Fn(&'a T) {}
ultronozm commented 4 years ago

A related issue shows up only when there are at least two reference arguments, but may be worked around by either annotating types or using helper functions:

trait Foo {
    fn call(&self, s: &str, t: &str);
}

impl<F: Fn(&str, &str)> Foo for F  {
    fn call(&self, s: &str, t: &str) {
        self(s,t);
    }
}

fn foo<F : Fn(&str, &str)>(f: F) {
    f("", "");
}

fn foo2<F: Foo>(f: F) {
    f.call("", "");
}

fn make_foo<'a, F: Fn(&str, &str) + 'a>(f: F) -> Box<dyn Foo + 'a> {
    Box::new(f)
}

fn foo3(f: Box<dyn Foo>) {
    f.call("", "");
}

fn main() {
    foo(|x,y| ()); // OK
    foo2(|x:&_,y:&_| ()); // OK
    foo2(|x,y| ()); // Error, but works with one argument
    foo3(Box::new(&|x,y| ())); // Error, even with one argument
    foo3(make_foo(|x,y| ())); // OK!
}
Aaron1011 commented 4 years ago

A similar case: This code compiles:

fn main () {
    let mut fields: Vec<&str> = Vec::new();
    let pusher = |a| fields.push(a);
}

but this code does not:

fn main () {
    let mut fields: Vec<&str> = Vec::new();
    let pusher = |a: &str| fields.push(a);
}
error: borrowed data cannot be stored outside of its closure
 --> src/main.rs:3:40
  |
2 |     let mut fields: Vec<&str> = Vec::new();
  |                         - cannot infer an appropriate lifetime...
3 |     let pusher = |a: &str| fields.push(a);
  |         ------   ---------             ^ cannot be stored outside of its closure
  |         |        |
  |         |        borrowed data cannot outlive this closure
  |         ...so that variable is valid at time of its declaration

error: aborting due to previous error

error: could not compile `playground`.

I think the explicit type annotation is causing a higher-ranked region to be inferred, while omitting the type annotation causes a concrete region to be inferred.

However, it's extremely surprising that copy-pasting the type from the definition of fields breaks the code.

c7hm4r commented 4 years ago

The new nightly rustc outputs some different error messages for the first post. The slightly adapted version …

pub struct Request<'a, 'b: 'a> {
    a: &'a (),
    b: &'b (),
}
pub trait Handler: Send + Sync + 'static {
    fn handle(&self, a: &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 dyn Handler) -> Box<dyn Handler> {
    Box::new(move |req| h.handle(req))
}

… leads to the following error:

error[E0308]: mismatched types
  --> src/a.rs:14:5
   |
14 |     Box::new(move |req| h.handle(req))
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected type `for<'r, 's, 't0> std::ops::Fn<(&'r mut a::Request<'s, 't0>,)>`
              found type `std::ops::Fn<(&mut a::Request<'_, '_>,)>`
c7hm4r commented 4 years ago

With nightly, the example provided by @gmorenz leads to the following clear bug:

error[E0308]: mismatched types
 --> src/a.rs:3:29
  |
3 |     vec![1,2,3].into_iter().find(suitable);
  |                             ^^^^ one type is more general than the other
  |
  = note: expected type `std::ops::FnOnce<(&i32,)>`
             found type `std::ops::FnOnce<(&i32,)>`
apiraino commented 3 years ago

Assigning P-medium as discussed as part of the Prioritization Working Group procedure and removing I-prioritize.

Additionally we nominate this longstanding issue to be discussed during the next meeting

spastorino commented 3 years ago

This issue was discussed during today's compiler team meeting

estebank commented 3 years ago

Triage: Current output:

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=456b5353ab48999eb273c83cfc0ae6f7

error[E0308]: mismatched types
  --> src/lib.rs:14:5
   |
14 |     Box::new(move |req| h.handle(req))
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected type `for<'r, 's, 't0> Fn<(&'r mut Request<'s, 't0>,)>`
              found type `Fn<(&mut Request<'_, '_>,)>`
note: this closure does not fulfill the lifetime requirements
  --> src/lib.rs:14:14
   |
14 |     Box::new(move |req| h.handle(req))
   |              ^^^^^^^^^^^^^^^^^^^^^^^^

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=8342936d11149e38cafc35da516274cb

error[E0521]: borrowed data escapes outside of closure
 --> src/main.rs:3:28
  |
2 |     let mut fields: Vec<&str> = Vec::new();
  |         ---------- `fields` declared here, outside of the closure body
3 |     let pusher = |a: &str| fields.push(a);
  |                   -        ^^^^^^^^^^^^^^ `a` escapes the closure body here
  |                   |
  |                   `a` is a reference that is only valid in the closure body

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=28594d6b627049e7a1de6cd590dfcae8

error[E0308]: mismatched types
  --> src/main.rs:30:5
   |
30 |     foo2(|x,y| ()); // Error, but works with one argument
   |     ^^^^ lifetime mismatch
   |
   = note: expected type `for<'r, 's> Fn<(&'r str, &'s str)>`
              found type `Fn<(&str, &str)>`
note: this closure does not fulfill the lifetime requirements
  --> src/main.rs:30:10
   |
30 |     foo2(|x,y| ()); // Error, but works with one argument
   |          ^^^^^^^^
note: the lifetime requirement is introduced here
  --> src/main.rs:15:12
   |
15 | fn foo2<F: Foo>(f: F) {
   |            ^^^

error: implementation of `Fn` is not general enough
  --> src/main.rs:31:10
   |
31 |     foo3(Box::new(&|x,y| ())); // Error, even with one argument
   |          ^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
   |
   = note: closure with signature `fn(&'2 str, &str)` must implement `Fn<(&'1 str, &str)>`, for any lifetime `'1`...
   = note: ...but it actually implements `Fn<(&'2 str, &str)>`, for some specific lifetime `'2`

error: implementation of `Fn` is not general enough
  --> src/main.rs:31:10
   |
31 |     foo3(Box::new(&|x,y| ())); // Error, even with one argument
   |          ^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
   |
   = note: closure with signature `fn(&str, &'2 str)` must implement `Fn<(&str, &'1 str)>`, for any lifetime `'1`...
   = note: ...but it actually implements `Fn<(&str, &'2 str)>`, for some specific lifetime `'2`

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=0254e96af9155a0a99880e06b4a75b95

error: implementation of `FnOnce` is not general enough
 --> src/main.rs:3:29
  |
3 |     vec![1,2,3].into_iter().find(suitable);
  |                             ^^^^ implementation of `FnOnce` is not general enough
  |
  = note: closure with signature `fn(&'2 i32) -> bool` must implement `FnOnce<(&'1 i32,)>`, for any lifetime `'1`...
  = note: ...but it actually implements `FnOnce<(&'2 i32,)>`, for some specific lifetime `'2`
Aaron1011 commented 2 years ago

Related: https://github.com/rust-lang/rust/issues/91966

108anup commented 10 months ago

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).

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 coerce<F>(closure: F) -> F
where F: for<'a> Fn(&'a My) -> &'a i32
{
    return closure
}

fn fun()
{
    // Does not work
    // let _f = |arg: &My| &arg.0;
    // call_with_ref(_f);

    // Workaround
    let _f = coerce(|arg: &My| &arg.0);
    call_with_ref(_f);

    // This is ok.
    call_with_ref(|arg: &My| &arg.0);
}

While in the above example, the closure can be passed to call_with_ref directly also and the problem seems contrived, this is not the case in the below example. Here the explicit coerce function seems helpful. I am guessing this will also work with Box instead of Option (for the request handling example in the issue).

fn optional_call_with_ref<F>(some_closure: Option<F>) -> i32
where
    F: for<'a> Fn(&'a My) -> &'a i32,
{
    let value = My(0);
    *(some_closure.unwrap())(&value)
}

fn fun2()
{
    // Does not work, have to use explicit coercion.
    // optional_call_with_ref(Some(|arg: &My| &arg.0));

    // Workaround
    let _f = coerce(|arg: &My| &arg.0);
    optional_call_with_ref(Some(_f));
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=893482858c3f6e96f464b052e1cf321d

I had three questions about this workaround:

  1. Is it possible to give a name to the trait bound for<'a> Fn(&'a My) -> &'a i32 so that multiple instances of this bound can use the name instead of repeating the definition.
  2. Is it possible to write a macro to generate the coerce function? Can macros accept a trait bound and a closure. E.g., to replace with let _f = coerce_macro!(for<'a> Fn(&'a My) -> &'a i32, |arg: &My| &arg.0)
  3. Is there a better workaround?

Disclaimer: I am fairly new to rust.

estebank commented 10 months ago

@108anup there's no way of being explicit yet in stable Rust, but nightly Rust supports specifying the for lifetime let _f = for<'a> |arg: &'a My| -> &'a i32 { &arg.0 };. Your work-around is what I'd use in stable.

douglas-raillard-arm commented 10 months ago

@108anup: Yes to 1. and 2.: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0a7258b5644e37aca7a25008d5ebca3d

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 for<'a>|&'a ...| syntax gets stabilized soon, nom (or I suppose any closure-heavy lib) is a very special kind of hell without that.

108anup commented 10 months ago

@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.

type SetRealValueFn<'a, 'ctx> = &'a dyn Fn(&'ctx z3::Context, RealNumRep) -> z3::ast::Bool<'ctx>;
type GetFeasibleRealValueFn<'a> = &'a dyn Fn(&z3::Solver) -> RealNumRep;
type RealNumInt = i32;
type RealNumRep = Ratio<RealNumInt>;

struct BinarySearchDetails<'a, 'ctx> {
    // https://stackoverflow.com/questions/27831944/how-do-i-store-a-closure-in-a-struct-in-rust
    name: &'a String,
    feasible: Option<RealNumRep>,
    lo: RealNumRep,
    hi: RealNumRep,
    set_value: SetRealValueFn<'a, 'ctx>,
    get_feasible_closure: GetFeasibleRealValueFn<'a>,
    use_int: bool,
}
douglas-raillard-arm commented 10 months ago

In unstable Rust you can with trait_alias feature: https://doc.rust-lang.org/beta/unstable-book/language-features/trait-alias.html

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=df05613e03998b6cf6a55824b48c1d89