0

I'm doing exercises in rustlings. In vecs2.rs, the code is:

fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
    for element in v.iter_mut() {
        // TODO: Fill this up so that each element in the Vec `v` is
        // multiplied by 2.

        element =  element * 2; // error here!!!


    }

    // At this point, `v` should be equal to [4, 8, 12, 16, 20].
    v
}

fn vec_map(v: &Vec<i32>) -> Vec<i32> {
    v.iter().map(|element| {
        // TODO: Do the same thing as above - but instead of mutating the
        // Vec, you can just return the new number!
        element * 2 // no error
    }).collect()
}

but I got an error:

error[E0369]: cannot multiply `&mut i32` by `{integer}`
  --> exercises/vecs/vecs2.rs:16:28
   |
16 |         element =  element * 2; // error here!!!
   |                    ------- ^ - {integer}
   |                    |
   |                    &mut i32
   |
help: `*` can be used on `i32` if you dereference the left-hand side
   |
16 |         element =  *element * 2; // error here!!!
   |                    +

I'm confused about why we should dereference explicit in vec_loop, but no need to dereference in vec_map (if so, it should be *element * 2). I'd confess that I always use reference like it is the value and never use like a pointer in other language( *some_ref ).

Archsx
  • 774
  • 1
  • 11
  • 19
  • _"but no need to dereference in `vec_map`"_ [`Add`](https://doc.rust-lang.org/stable/std/ops/trait.Add.html#tymethod.add) [`Mul`](https://doc.rust-lang.org/stable/std/ops/trait.Mul.html) and other arithmetic traits are also implemented for reference types. – E_net4 Aug 11 '23 at 11:10

1 Answers1

2

I'd confess that I always use reference like it is the value and never use like a pointer in other language(*some_ref).

A reference is always a pointer so technically this is wrong, however practically it often works for two reasons:

  1. . automatically derefs, so there's no need to deref' when accessing an attribute or calling a method (it would be rather inconvenient due to the precedence too)
  2. what you're getting confused by here is that it's common for traits to be partially or fully implemented on references for convenience

In this case, if you go check the implementations of Mul you will see:

impl Mul<&i32> for &i32
impl Mul<&i32> for i32
impl Mul<i32> for i32
impl Mul<i32> for &i32

So for convenience the standard library provides an implementation of * for every combination of i32 and &i32.

Note, however, that there is no such implementation for &mut i32, which is an entirely separate type. And the type of element in the loop, as that's what iter_mut() hands out:

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T
    ...
}

Which is why you can't just multiply an &mut i32 with an i32.

Note that it still won't work if you dereference element, because now you're trying to assign an i32 (the result of your multiplication) to a variable of type &mut i32 (which furthermore is not itself mut). So you'll have to write:

*element = *element * 2;

at which point you can just write the more obvious

*element *= 2;

which goes through MulAssign, which expects a mutable LHS, and thus can use either of the existing implementations:

impl MulAssign<&i32> for i32
impl MulAssign<i32> for i32

Note that there's still no MulAssign<&mut i32>, so *expression *= expression still fail to compile, which I find funny (though it's a rare issue to encounter).

Masklinn
  • 34,759
  • 3
  • 38
  • 57
  • thx! it is amazing that &i32 and &mut i32 are different types, which I never know before. – Archsx Aug 11 '23 at 11:23
  • While the syntax doesn't necessarily look it, you should think of `&T` and `&mut T` as `Ref` and `RefMut`, they're really smart pointers (although all of their smarts are compile-time constraints), but built very deep into the language and with a lot of special handling (tbf `Box` also has a few things which only it can do). – Masklinn Aug 11 '23 at 12:16