`cannot assign to this expression` with `AddAssign`

Hello, could someone enlighten my why AddAssign does not work with an "unnamed return value"?

#[derive(Debug)]
struct Foo(i32);

impl Foo {
    fn access<'a>(&'a mut self) -> Accessor<'a> {
        Accessor(&mut self.0)
    }
}

struct Accessor<'a>(&'a mut i32);

impl<'a> core::ops::AddAssign<i32> for Accessor<'a> {
    fn add_assign(&mut self, other: i32) {
        *self.0 += other;
    }
}

fn main() {
    let mut foo = Foo(0);
    
    let mut access = foo.access();
    access += 5; // OK
    println!("{foo:?}");
    
    foo.access() += 5; // ERROR: cannot assign to this expression
    println!("{foo:?}");
}

Playground link

I guess the left hand size of an compound assignment is a mutable place expression:

The syntax of compound assignment is a mutable place expression, the assigned operand, then one of the operators followed by an = as a single token (no whitespace), and then a value expression, the modifying operand.

If you follow the link you'll see a place expression is:

A place expression is an expression that represents a memory location. These expressions are paths which refer to local variables, static variables, dereferences (*expr), array indexing expressions (expr[expr]), field references (expr.f) and parenthesized place expressions. All other expressions are value expressions.

Seems like the error message could be improved to mention a place expression.

Edit: However, the rustc --explain E0067 does mention this:

You need to have a place expression to be able to assign it something.
3 Likes

If you need a one-liner, this happens to work:

*(&mut foo.access()) += 5;
1 Like

Ok, so I guess then one solution is to implement DerefMut for the accessor type?

impl<'a> core::ops::Deref for Accessor<'a> {
    type Target = i32;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<'a> core::ops::DerefMut for Accessor<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

fn main() {
    let mut foo = Foo(0);
    *foo.access() += 5;
    println!("{foo:?}");
}

Playground link

However that is only possible if there is an "underlying" type (like in this trivial example). I was looking to provide rich operator overloads for view types. For example used in linear algebra / scientific computing to operate on tensor slices.

Have you thought about using Index and IndexMut instead of your access fn? I haven't tried it for anything like this, just a thought.

1 Like

Similar limitations to DerefMut when trying to replace the method.

Here's a collection of workarounds, including questionable ones.

    *foo.access().as_mut() += 5;    // Accessor<'_>: AsMut<Self>
    let _ = foo.access() + 5;       // Accessor<'_>: Add<i32, Output = Self>
    *&mut foo.access() += 5;        // (deref expression) 
    (foo.access(),).0 += 5;         // (field expression)
    [foo.access()][0] += 5;         // (index expression)
    drop(foo.access() + 5);         // Accessor<'_>: Add<i32, Output = Self> (again)
    foo.access()[()] += 5;          // Accessor<'_>: IndexMut<(), Output = Self>
    access!(foo) += 5;              // (macro'd index expression)
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.