UX guidelines for missing operator trait implementations


#1

Abstract

Currently the compiler does not emit a clear message when specific operator traits are not implemented.

Example

use std::collections::HashMap;
use std::ops::{Index, IndexMut};

struct Foo {
    pub x: u64,
}

impl<'a> Index<&'a u64> for Foo {
    type Output = u64;

    fn index<'b>(&'b self, _index: &'a u64) -> &'b u64 {
        &self.x
    }
}

impl<'a> IndexMut<&'a u64> for Foo {
    fn index_mut<'b>(&'b mut self, _index: &'a u64) -> &'b mut u64 {
        &mut self.x
    }
}

fn main() {
    let mut foo = Foo{x: 1};
    let pos: u64 = 1;
    *(&mut foo[&pos]) = 2;


    let mut map: HashMap<u64, u64> = HashMap::new();
    *(&mut map[&pos]) = 2;
}

Foo implements the IndexMut trait similar to the one which a newcomers would expect from HashMap. The shown code works perfectly. Now let’s have a look at the error that rustc emits for our wrong usage of the hash map:

src/main.rs:29:12: 29:21 error: cannot borrow immutable indexed content as mutable
src/main.rs:29     *(&mut map[&pos]) = 2;
                          ^~~~~~~~~

This is very similar to what the borrow checker usually tells us when there is an error. The message does not contain any indicator that HashMap does not implement the relevant operator. This makes it very hard to figure out why the shown code is not working. Clearly someone could look at the API documentation of HashMap but when you expect that a type implements an operator, you usually look for another reason. In this case, community websites like the Rust user forum or StackOverflow are required to get the right hint.

To make it easier for users to get a the right hint, the following error would be more helpful:

src/main.rs:29:12: 29:21 error: indexing operation in a mutual context is not possible here because HashMap does not implement IndexMut
src/main.rs:29     *(&mut map[&pos]) = 2;
                          ^~~~~~~~~

Given this error it would be easy to lookup IndexMut within the API docs, see that we indeed requested the right operator but that HashMap does not implement it.

The generic problem

It seems that the compiler does not have a standardized way to tell users about missing operator implementations, no matter if they are accidentally missing or the users requested an operation that just would not work with the given type. The question that arises here is if and how the compiler should deal with this problem and if this is already covered by some RFC or not.


#2

I definitely think the compiler should specialize these errors somewhat. In general, the borrowck code leaks some abstractions of this kind. Given that we are likely to be rewriting borrowck to work on MIR soon, we should probably think a bit about how to solve it in that context. I expect this may require some minor modifications to MIR to try and preserve a bit more information about what the code was pre-desugaring – for example, we may want to remember when derefs were “automatically inserted” or explicit, or in this case the fact that a method call was derived from a operator.

That said, I expect we can also make good progress just by changing the wording of borrowck errors. Currently they are sometimes more specific than necessary and also somewhat jargon heavy, in my opinion.