Follow up: Why deferencing moves but not always?

The user in this post states:

*x will create a place expression (think of this as an lvalue from C++). If you use this place expression directly, you will either have to move or copy it. This is why *nd as i32 == 0 fails. However, if you use it indirectly, i.e. by accessing a field, then you don't have to move or copy the entire place expression (only the parts that are used). This is why (*cd).field == 0 succeeds, it only needs to access field (which is Copy )

The user says that if I used the place expression directly, it will have to be moved or borrowed. Why does the following not cause a move?

enum CannotDeref {
    Entry = 1,
}

#[derive(PartialEq)]
struct CanDeref {
    field: i32,
}
let cd = &CanDeref{field: 1};
let pd = &CanDeref{field: 2};

if *cd == *pd { //<= how come no move occurs if we're using the expression directly?
};

The original question was, why is there a move in one case and not in the other, when de-referencing?

fn main() {
    let nd = &CannotDeref::Entry;
    let cd = &CanDeref{field: 0};
    
    if *nd as i32 == 0 { // <- A move occurs here and generates following error:
                         //    move occurs because `*nd` has type `CannotDeref`, which does not implement the `Copy` trait
    };
    
    if (*cd).field == 0 { // <- This is ok, no move occurs here
    };
}

a == b is desugared to PartialEq::eq(&a, &b) because it would be pretty lame if equality comparison moved the values being compared.

1 Like

Why is there a move for this equality comparison then?

  if *nd as i32 == 0 { // <- A move occurs here and generates following error:
                         //    move occurs because `*nd` has type `CannotDeref`, which does not implement the `Copy` trait
    };
1 Like

In *a == *b, the code is desugared into &*a == &*b, so the move is "cancelled out" (there's probably a better term for it. Reborrowing maybe?).

In *b as i32 == 0, the code is desugared into &(*b as i32) == &0; meaning that you first move the value, then convert it into i32, and then borrow the temporary result, so the move cannot be avoided.

Edit: errr, meant to say that is desugared into PartialEq::eq(&*a ,...), but it's hard tk type that on mobile

1 Like

That makes sense. Does the move happen because of the conversion or the borrowing from the temporary or both?

My intuition is that the conversion "uses up" the lvalue and causes a move, but I'm sure someone more knowledgeable will be able to correct me.

Yes, it's due to the cast. See also.

let nd = &CannotDeref::Entry;
// Okay
match *nd { _ => {} }
// error[E0507]: cannot move out of `*nd` which is behind a shared reference
match *nd as i32 { _ => {} }
1 Like

It's not because of the equality comparison, it's because of the cast. *nd is a place expression, because it merely designates the variable pointed by nd, so &*nd is the same as nd. In contrast, *nd as i32 is not a place expression: instead of referencing the dereferenced pointer again immediately, you are attempting to convert its value. (By the way, that as cast wouldn't work, either: it's for conversion between primitives, it can't guess that you want it to yield the single field of your struct.)

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.