Skip to content
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

closure compilation fails depending on type inference #64373

Open
chlobes opened this issue Sep 11, 2019 · 2 comments
Open

closure compilation fails depending on type inference #64373

chlobes opened this issue Sep 11, 2019 · 2 comments
Labels
A-closures Area: Closures (`|…| { … }`) A-inference Area: Type inference

Comments

@chlobes
Copy link

chlobes commented Sep 11, 2019

fn foo(_: &mut String) {}

fn bug1() {
    let _ = |a: &mut String| for _ in 0..0 {
        foo(a)
    }; //works fine
    let _ = |a| for _ in 0..0 {
        foo(a)
    }; //fails
}

fn bug2() {
    let mut a = String::new();
    let mut b = String::new();
    let f = |a: &mut String| foo(a);
    let g = |b| foo(b);
    f(&mut a); //works
    f(&mut a); //works
    g(&mut b); //works
    g(&mut b); //fails
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0382]: use of moved value: `a`
 --> src/lib.rs:8:13
  |
7 |     let _ = |a| for _ in 0..0 {
  |              - move occurs because `a` has type `&mut std::string::String`, which does not implement the `Copy` trait
8 |         foo(a)
  |             ^ value moved here, in previous iteration of loop

error[E0499]: cannot borrow `b` as mutable more than once at a time
  --> src/lib.rs:20:7
   |
19 |     g(&mut b); //works
   |       ------ first mutable borrow occurs here
20 |     g(&mut b); //fails
   |     - ^^^^^^ second mutable borrow occurs here
   |     |
   |     first borrow later used by call

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0382, E0499.
For more information about an error, try `rustc --explain E0382`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

@jonas-schievink
Copy link
Contributor

I've definitely seen this reported in another issue before, but can't seem to find it

@Mark-Simulacrum Mark-Simulacrum added A-closures Area: Closures (`|…| { … }`) A-inference Area: Type inference labels Sep 14, 2019
@ExpHP
Copy link
Contributor

ExpHP commented Sep 29, 2021

Dupe of #41078


This is a consequence of the fact that a closure is only given higher-ranked trait bound (HRTB) lifetimes if the presence of these lifetimes in its signature can be seen at the time the closure is type-checked.

Note that the full |a: &mut String| is not necessary, only the |a: &mut _|. Basically:

  • |a| foo(a) will create an fn(?A) -> ?B, where ?A and ?B are inference variables (types to be filled in later)
  • |a: &mut _| foo (a) will create an fn(&mut ?A) -> ?B, which is shorthand for for<'a> fn(&'a mut ?A) -> ?B. The for<'a> is the key missing piece.

On hearing this you might wonder why you've never had a similar problem with things like slice.iter_mut().map(|x| foo(x)), and that's because the compiler has a special case when type checking closures that appear as function arguments (it will infer types from Fn bounds). So this problem (and many other related problems with type inference) tends to rear its ugly head only in comparatively uncommon scenarios, such as when assigning a closure to a local variable with let.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: Closures (`|…| { … }`) A-inference Area: Type inference
Projects
None yet
Development

No branches or pull requests

4 participants