Is this compiler error message misleading, or is it just me?

Consider this bare-bones example

type T  = usize;
type U1 = String;
type U2 = bool;

pub struct U {
    pub one: U1, // not Copy
    pub two: U2,
}

fn doit(u_instance: U) -> Vec<U2> {
    let _moved: U1 = u_instance.one;
    Vec::<T>::new()
        .iter()
        .map(|_completely_unrelated_to_u_instance: &T| u_instance.two)
        .collect()
}

The error message that the compiler generates (shown in full, below), highlights 3 portions of the code:

  1. line 14: |_completely_unrelated_to_u_instance: &T| (value borrowed here after partial move)

  2. line 11: u_instance.one (value partially moved here)

  3. line 14: u_instance (borrow occurs due to use in closure)

In my shell 1. is highlighted in bright red underlined with heavy ^^^^^^, while 2. & 3. are highlighted in a more toned-down blue and underlined with lighter -----

I think that 2. and 3. are spot on, completely relevant and helpful in understanding the problem, while 1. is a red herring ... and yet the compiler makes the most noise about it.

Am I missing some subtlety which would explain why 1. is a good message? I appreciate that the closure is trying to borrow u_instance (and that it can't because it has already been partially moved) but the borrowing is not happening in the closure's parameter list, which is where the message claims it is happening.

In short, I think that the message would be much better without 1.

Full error message

For completeness, here is the error message in full:

error[E0382]: borrow of partially moved value: `u_instance`
  --> src/lib.rs:14:14
   |
11 |     let _moved: U1 = u_instance.one;
   |                      -------------- value partially moved here
...
14 |         .map(|_completely_unrelated_to_u_instance: &T| u_instance.two)
   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ---------- borrow occurs due to use in closure
   |              |
   |              value borrowed here after partial move
   |
   = note: partial move occurs because `u_instance.one` has type `String`, which does not implement the `Copy` trait

2 Likes

(emphasis mine) :clap:


More seriously, I agree with your remark, and these kind of bugs can and should be reported to their repo, with a A-diag D-confusing labels (@rustbot set labels to A-diag D-confusing).

I think the error message wanted to span over the closure, and it was decided that spanning over the closure parameters in that case sufficed. Making it span over the whole closure, albeit a bit mouthful, would be a better indicator of there being an issue with the closure.

1 Like

I see error message as just fine. It is the construction of the closure that is at fault. The fix is likely a change to the closure; remove the borrow from its body.

Without the closure highlighted you glance at two partial moves which are legitimate on their own.

edit: I guess emphasis could be shifted away from the parameters by changing the inner ^s to construction when space permits. Seeing both |s in the error helps

3 Likes

Hmm... What would highlighting the "whole" closure look like when it spans over multiple lines?

I tentatively disagree with this, because it's important that u_instance is borrowed by the closure itself, and not just the one line on which it is used; i.e. if you had something more like

fn doit(u_instance: U) -> Vec<U2> {
    let _moved: U1 = u_instance.one;
    Vec::<T>::new()
        .iter()
        .map(|_completely_unrelated_to_u_instance: &T| {
            front_matter();
            do_something_with(u_instance.two);
            get_result()
        })
        .collect()
}

it's important to identify that the thing that's borrowing u_instance is the thing being passed to map, and not just the line calling do_something_with -- if the error message just highlights the two lines containing uses of u_instance.one and u_instance.two, it's not clear that the reason for the error is because u_instance has been borrowed in its entirety by a closure which is not being highlighted at all.

Highlighting the whole closure, rather than just its argument list, is something I could get behind. Not highlighting the closure at all would be a mistake IMO.


P.S. The original code should compile without error when RFC 2229 is implemented.

3 Likes