8

How to make sense of the following piece of code? I'm new to Rust but have background on C/Haskell and a little bit C++. The only reference I can find is to deref coercions.

fn main() {
    let xs: [u32; 4] = [0, 1, 2, 3];
    let mut i: u32 = 0;
    for x in xs.iter() {
        if i > *x {     // It looks x is an iterator. Understood.            
            i = i + x;  // no error. (coerced)
                        //Quote: "Rust will do this as many times
                        //       as possible until the types match."
            i = i + *x; // no error (explicit deref)
            i += x;     // error about u32/&u32 mismatch. Why the magic failed?
            i += *x;    // no error (explicit deref)
        }
    }
    println!("{}", i);
}
Shepmaster
  • 274,917
  • 47
  • 731
  • 969
wuxb
  • 2,512
  • 1
  • 18
  • 30

1 Answers1

12

There is no auto-deref or coercion here, i + x works simply because u32 implements both Add<u32> and Add<&u32>. If you check the docs for u32, you'll find the following four trait impls:

impl Add<u32> for u32
impl<'a> Add<u32> for &'a u32
impl<'a> Add<&'a u32> for u32
impl<'a, 'b> Add<&'a u32> for &'b u32

u32 only implements AddAssign<u32> but not AddAssign<&u32> (this is a bug and will be fixed in 1.18 or 1.19 fixing it causes regression which means this impl probably needs to wait for Rust 2.0), so i += x is an error.

impl AddAssign<u32> for u32
//impl<'a> AddAssign<&'a u32> for u32 <-- is missing.

Why does auto-dereferencing not happen? — Auto-deref only happens when it is a receiver i.e. the "self" in a method call foo.bar(). x is not a "self" argument and + is not a method call. So there's no auto-deref here. See What are Rust's exact auto-dereferencing rules? for detail.

Community
  • 1
  • 1
kennytm
  • 469,458
  • 94
  • 1,022
  • 977
  • Thanks your your answer and the reference. – wuxb Apr 12 '17 at 06:40
  • 1
    I thought that `a + b` was sugar for `a.add(b)` (with `add` coming from `std::ops::Add`). If that were the case, `a + b` *would* be a method call with `a` being self, and auto-dereferencing would apply to operators. I wonder if there is a drawback to this approach because of which Rust chooses not to define operators like that. – user4815162342 Apr 12 '17 at 07:11
  • @user4815162342 there is auto-dereferencing, but it applies only to `a` – torkleyy Apr 12 '17 at 11:41
  • @torkleyy No auto-deref won't be done between operators, e.g. `&mut u32 += u32` doesn't work. Which is why there are four impls for Add instead of two. – kennytm Apr 12 '17 at 11:55