Little issue about deref


#1

Why is *v moved, and *v == Foo is not?

Example:

#[derive(PartialEq)]
enum Foo {
    Bar,
    Baz,
}

fn main() {
    let ref_foo = &Foo::Bar;

    // Not a move.
    if *ref_foo == Foo::Bar {
        println!("hey!");
    }

    // Clearly a move.
    *ref_foo;
}

#2

Because PartialEq takes a borrow of its arguments:

pub trait PartialEq<Rhs = Self> where Rhs: ?Sized {
    fn eq(&self, other: &Rhs) -> bool;

    fn ne(&self, other: &Rhs) -> bool { ... }
}

Hence:

*ref_foo == Foo::Bar
// desugars as
std::cmp::PartialEq::eq(&*ref_foo, &Foo::Bar)

This is a borrow, not a move.


#3

So the compiler interprets &*ref_foo as ref_foo?

And whenever I do &*, it will be the binding itself (in case of references)?

edit: One more question: Why is this not a move?

let foo = &Foo::Bar;

match *foo {
    Foo::Bar => {},
    _ => {},
}

#4

Yep!

As long as the result of accessing something on a struct or enum (or the struct/enum itself) is a reference, it doesn’t move the struct.

For example, this will work:

enum Foo {
    Bar,
    Baz,
}

struct Bax {
    foo: Foo,
}

fn main() {
    let bax = &Bax { foo: Foo::Bar };

    let foo = &bax.foo;
    let foo2 = &bax.foo;
    // or, using pattern matching (this is exactly equivalent to the statement above)
    let ref foo3 = bax.foo;
}

Pattern matching can also be used for borrowing members in enum variants in a match statement without moving the enum:

enum Foo {
    Bar,
    Baz,
}

enum Bax {
    Foo1(Foo),
    Foo2(Foo),
}

fn thing_with_foo(_foo: &Foo) {
}

fn main() {
    let bax = &Bax::Foo1(Foo::Bar);

    match *bax {
        Bax::Foo1(ref foo) => {
            thing_with_foo(foo);   
        },
        Bax::Foo2(ref foo) => {
            unreachable!();
        },
    }
}

#5

Yeah, I’ve known about this, I just don’t understand the reason of

match *bax {
   Bax::Bar => {}
}

being not a move.

But, thank you anyway! :smile:


#6

You’re not actually binding anything. You’re just comparing the contents of bax against the variant Bax::Bar, which doesn’t need to take ownership of the data. If you had Bax::Bar(x) => ..., then you would be moving *bax, since you’re assigning part of it to a new binding (x). But then, if you had Bax::Bar(ref x), then it wouldn’t be a move, since you’re only borrowing part of *bax.

A move happens when you assign a non-copy value to a variable, pass it to a function, return it from a function, or (I think) have it as the ignored value of an expression used as a statement. It doesn’t happen if you borrow the value in some way, such as using & or ref or doing an operation that implicitly borrows it’s arguments, like comparison.

Dereferencing really doesn’t have anything to do with it, except for the fact that shared references can be copied, so you won’t observe move behaviour with them unless you dereference them. You can observe move behaviour with any non-Copy struct without any references involved,