About "Rust automatically adds in & , &mut , or *"

According to "The Rust Programming Language" ch05-03
Here’s how it works: When you call a method with object.something() , Rust automatically adds in & , &mut , or * so that object matches the signature of the method. In other words, the following are the same:

My question is about this code

let value = Rc::new(RefCell::new(5));
*value.borrow_mut() += 10;

I can understand why value.borrow_mut() will automatic compile to (*value).borrow_mut()
But why I should add * before value.borrow_mut() for the next add operation

This is the docs about += operator

fn add_assign(&mut self, rhs: Rhs);

In my understanding *value.borrow_mut() += 10; equals to

*value.borrow_mut().add_assign(10);

It still call a method,so why the compiler doesn't automatic add * before value.borrow_mut()?

It just like this example

let mut a = 3;
let mut b = &mut a;
*b += 1;
// Why B should add * before

If I am wrong, please point it out.
Thanks for your answer

2 Likes

I think it's just that the auto deref works for method calls. You are confusing operator syntax and method calls, even though internally '+=' is dispatched to add_assign(), I think the book makes it clear that this is something that only works with method calls (I guess the compiler doesn't infer directly from the operator syntax). Also I don't think &mut a implements AddAssign here, these are are only implemented for primitive types themselves.

1 Like

there are two kinds of things going on here, which are very different.

Method calls

mehtods calls are of the form expr.method(args), and they do add &, &mut, and * automatically until they find a type match for the method call.

The XAssign operators

these operators are used as follows place X= expr;. a place expression is a kind of expression one can assign to. neither the XAssign operators no the regular binary operator insert any &, &mut, or * to find the correct type. they only accept the type as is

when you implement the XAssign operator, you do write fn x_assign(&mut self, rhs: Rhs), but when you use them as operator, it will add the &mut. so
a += b becomes AddAssign::add_assigned(&mut a, b) !
this is because it works best with local variables.

so a += b only works if A : AddAssign<B>, which is the cleanest.

value.borrow_mut() is a &mut i32, so you must convert it t an i32 to use += on it

that is wrong
*value.borrow_mut() += 10;
equals to

AddAssign::add_assign(&mut (*value.borrow_mut()), 10)

it is not a method call and does not automatically dereference.

4 Likes

Thanks for your help

And I want to ask
Is it only expr.method(args) , compiler will add & , &mut , and * automatically until they find a type match for the method call.
Is there any syntactic sugar can do the same thing?
Thanks

not in general, but on certain coercion sites rust can implicitly transform one reference into another as if through similar inserts. most common being function arguments and function output :

fn works1<'a>(x : &&&'a str) -> &'a str {
    x
}

fn works2<'a>(x : &&&'a String) -> &'a str {
    x
}

fn works3<'a>(x : &'a mut &'a mut &'a mut String) -> &'a mut str {
    x
}

fn works4<'a>(x : &'a mut Box<&'a mut String>) -> &'a mut str {
    x
}


fn doesntwork1<'a>(x : Box<&'a mut &'a mut String>) -> &'a mut str {
    &mut x // doesn't work because `Box` isn't a reference
    // *x would work
    // the compiler adivises using `&mut x`. while this would allow the coercion, 
    // the lifetimes wouldn't work out and the function wouldn't compile
}


fn doesntwork2(x : &&&u8) -> u8 {
    x // doesn't work because `u8` isn't a reference
    // only ***x would work
}

note that these can only remove reference layers, not add more

fn takes_str(_ : &str) {}
fn takes_str_mut(_ : &mut str) {}

fn works1b<'a>(x : &&&'a str){
    takes_str(x)
}


fn works2b<'a>(x : &&&'a String) {
    takes_str(x)
}

fn works3b<'a>(x : &'a mut &'a mut &'a mut String) {
    takes_str_mut(x)
}

fn works4b<'a>(x : &'a mut Box<&'a mut String>){
    takes_str_mut(x)
}

fn doesntwork1b<'a>(x : &'a &'a String){
    works2b(x) // needs &x
}

If this understanding came from reading something like

the following two statements are equivalent:

fn f<T: AddAssign + Copy>(mut x: T, y: T) {
    x += y; // Statement 1.
    x.add_assign(y); // Statement 2.
}

it is because the documentation is sloppily phrased. Operators aren't desugarings to other Rust source code; there are cases where the operator resolution doesn't correspond to any desugaring AFAIK.

Or in short: don't take statements about Rust expressions being "equivalent" too literally.

OK
Thanks for your help