Confusions between &T and &mut T in terms of numeric std::ops traits

I came across some snippets about "numeric" operations +, & etc (std::ops - Rust) and find out Rust has the weird treatment of the borrowed reference type.

For example, the code below runs correctly, but the commented two statements are disallowed.

fn main() {
    let mut arr = [1u8, 2, 3, 4];
    {
        let pos: &u8 = unsafe { arr.get_unchecked(2) };
        let v: u8 = pos + &1u8;
        let w: u8 = &2u8 + pos;
        println!("v={}, w={}", v, w);
    }
    {
        let pos: &mut u8 = unsafe { arr.get_unchecked_mut(2) };
        // let v = pos + 1u8;
        // *pos += &1u8;
        let v: u8 = *pos + &1u8;
        let w: u8 = &2u8 + *pos;
        println!("v={}, w={}", v, w);
    }
}

Also here Rust Playground

My question is that why Rust allows operations between "&T op T", "&T op &T", and "T op &T" (and the resulting type is T) where T is a primitive numeric type, but doesn't allow "&mut T op T" etc.
This to me seems rather unintuitive and I would expect that Rust requires programmers to be more explicit. Is there a particular reason that forces Rust to behave like this?

Looks like nobody implemented Add for a &mut {integer}, probably because it's extra noise for no real gain: mutable refs aren't as common as values or immutable refs for these types, and the "workaround" of deref'ing is simple enough. But maybe there's a more principled reason.

fn increment_key(&mut self, key: K) {
    let count = self.map.entry(key).or_insert(0);
    count + 1;
}

Edit: Actually, you can still just as easily write *count + 1 so _/o\_

Do you mean something like this?

use std::collections::HashMap;
fn increment_key(key: u8) {
    let mut map = HashMap::new();
    map.insert(1u8, 1u8);
    let count = map.entry(key).or_insert(0);
    let v = count + 1u8;
    println!("{}", v);
}

It cannot compile.

1 Like

Actually I think "&T op T" etc also bring no gain, but just surprise to me :frowning:

Maybe. Immutable references feature heavily in Rust though. Say iterating over an immutable slice. You can of course dereference and/or pattern match away the references but it becomes a bit boilerplate-y at times, particularly with iterator chains. That's why things like Iterator in std::iter - Rust exist too.