What are the rules for implicit vs. explicit deref?

I'm trying to understand the rules for when I need to explicitly dereference using the * operator and when I do not. Here's an example:

fn ch_15_2_a() {
    let x = 2;
    let y = &x;
    // Notice that y is auto-derefed
    // Question: Is that due to "implicit deref coercion", or is it due to some
    // other Rust rule that I don't know about yet?
    let z = x + y;

    assert_eq!(2, x);
    // Must explicitly deref y here or compile error:
    // no implementation for `{integer} == &{integer}`
    assert_eq!(2, *y);

    // Notice that y is auto-derefed again
    println!("{} + {} = {}", x, y, z);
}

In the example above (which compiles and runs Ok) I can use y without dereferencing it in the x + y expression and when passing it to println!(), but not when passing it to assert_eq!(). Why?

Here is another example (which also compiles and runs Ok):

fn ch_15_2_b() {
    let x = 5;
    let y = Box::new(x);
    // Rust doesn't auto-deref Box<i32> like it did &i32, so this gets a
    // compile error:
    // let z = x + y;
    // I have to explicitly dereference y like this:
    let z = x + *y;
    
    // I don't have to dereference y here:
    println!("{} + {} = {}", x, y, z);
}

This is like the previous example except y is a Box<i32> instead of &i32. My understanding is a Box<T> is like a reference, it just points to memory on the heap instead of on the stack. However this time I have to dereference it in an arithmetic expression: x + *y instead of x + y. But still I can pass it to println!() without dereferencing it. Can someone please explain the rules behind when I need to explicitly dereference and when I don't?

Thanks!
David H

In this particular example, I think the behavior in the first and third instances can be explained by these impls:

impl<'a> Add<i32> for &'a i32

(at https://doc.rust-lang.org/std/ops/trait.Add.html#method.add-88)

This allows adding i32 with &i32 (there is a similar impl for adding &i32 with `i32).

impl<'_, T> Display for &'_ T where
    T: Display + ?Sized

(at https://doc.rust-lang.org/std/fmt/trait.Display.html#method.fmt-81)

This allows displaying a reference to anything that you can display.


Notably, neither of these are implicit deref. It's just that the operator in question accepts a reference. It could be different for a different type, or different operator - std just includes impls which handle these situations for convenience.

There is just no implementation impl<'a, T> PartialEq<&'a T> for T where T: PartialEq<T> which would allow the second situation.

I'm not sure I can offer general advice for this - the best way would be to get to know the traits? One thing to consider is that if an operation only needs to read the value, and it's cheap to do that, there is generally a blanket implementation for &'_ T where T: XXX like the one for Display.

As for auto-deref, the only situation I know it to apply in is when the & operator is used, the target type is known, and the target type can be reached via Deref.

There are two things going on in this one:

1 Like

@daboross @scottmcm Thanks for supplying some missing understanding that I didn't get from the first 15 chapters of the Rust book: the + operator actually involves a trait (I wouldn't have known where to go looking for that trait) and the trait has implementations where the first and second parameters are not the same type, which I would not have suspected. And I learned a little more what macros can do (haven't gotten to the macro chapter yet). I'm looking forward to learning more. Thanks for the help!

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.