Avoid dereferencing guard with overloadable operators

use std::ops::AddAssign;
use std::marker::PhantomData;

struct Guard<T> {
    _p: PhantomData<T>
}

impl<T: AddAssign> AddAssign<T> for Guard<T> {
    fn add_assign(&mut self, rhs: T) {
        todo!();
    }
}

fn main() {
    //
    // This doesn't compile.
    
    Guard {
        _p: PhantomData,
    } as Guard<isize> += 1; // error[E0067]: invalid left-hand side of assignment
    
    //
    // But this does (notice the `mut`).
    
    let mut guard = Guard {
        _p: PhantomData,
    } as Guard<isize>;
    
    guard += 1;
    
    //
    // Actual use-case would be something like:
    
    // ctx.state_mut() += 1; // `state_mut()` returns that `Guard`
    //                       // and we get the same compile error E0067.
    
    // *ctx.state_mut() += 1; // I am trying to make it work without the 
    //                        // need to dereference the `Guard`.
}

Is this something compiler could accept but it just doesn't, or is there some other reason behind it?

Not sure what are you trying to achieve there. Note that first piece destroys guard immediately after assignment.

Thus if compiler would make it possible to use such syntax this would lead to very dangerous code which doesn't do what people expect it to do.

1 Like

The left-hand side of a compound assignment operator (like +=) is required to be a place expression. This restricts it to variable names, fields, reference targets, etc— Generally locations that can be assigned to. If your goal is to make a temporary Guard, modify it, and then drop it all in one line, you can write something like this:

    *&mut (Guard {
        _p: PhantomData,
    } as Guard<isize>) += 1; 

You can use DerefMut to make this work, but lots of people will consider it an abuse of that trait:

Edit: I was confused; this doesn't work (and, on reflection, must be what you already tried before asking your question :confused: )

Hmm, I should have made an analogous example using RefCell to make it more obvious what I want to achieve. What I was thinking about is implementing AddAsign for RefMut (received from RefCell::borrow_mut()) to allow one liners like: ref_cell.borrow_mut() += 1 (instead of *ref_cell.borrow_mut() += 1).

Yup, that's what I want to happen.

Oh, so because Guard is the "value expression" it doesn't work...

Yup, I tried that haha. Really I was just wondering why I get that E0067 error for the "actual use-case" when it looked rather correct. :sweat_smile:

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.