简体   繁体   中英

Understanding move semantics for enums Rust

So I'm looking at the impl for is_some() for Option and I noticed that it uses match *self {} under the hood...so it moves it internally.

My question is, if it gets moved, how am I able to do something like this? https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f094da12290b77bad526674467e51043

fn main() {
    let x = Option::Some(3);
    x.is_some();
    x.is_some();
}

My expectation is that I should be able to call is_some() only once, and the next time I call it I should get some sort of error saying it's been moved...but no, it all compiles fine.

What am I misunderstanding?

*self in match *self { ... } does not move (or copy) what self points to. From " The Rust Reference " (emphasis mine),

A match behaves differently depending on whether or not the scrutinee expression is a place expression or value expression. If the scrutinee expression is a value expression, it is first evaluated into a temporary location, ...

When the scrutinee expression is a place expression, the match does not allocate a temporary location; however, a by-value binding may copy or move from the memory location. ...

*self is a place expression. From " The Rust Reference " (emphasis mine),

Expressions are divided into two main categories: place expressions and value expressions. ...

A place expression is an expression that represents a memory location. These expressions are paths which refer to local variables, static variables, dereferences ( *expr ) , array indexing expressions ( expr[expr] ), field references ( expr.f ) and parenthesized place expressions. All other expressions are value expressions.

A value expression is an expression that represents an actual value.


You might also be interested to know that the Some(_) => true arm in the match body does not bind anything. From " The Rust Reference ",

Unlike identifier patterns, it does not copy, move or borrow the value it matches.

where "it" means the wildcard pattern ( _ ).

(See the @dkim's answer for a more official and cited answer)

If the signature of the function accepts by reference, it does not take ownership of the value. Option.is_some() actually takes &self not self.

The interesting part is how *self is allowed to be used in a function that receives &self when Self: Copy is not bounded.

To test this, let's create a minimal example containing something similar: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d7b137b74b5cd8f8bb57398ae01bf4e3

#[derive(Debug)]
pub enum A {
    X(String),
    Y(i32),
}
pub fn f(a: &A) {
    match *a {
        A::X(_) => {
            //  dbg!(s);
        }
        A::Y(_i) => {
            //  dbg!(i);
        }
    };
}

This compiles fine. But let's change the A::X(_) pattern to A::X(_s) : https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=93030e5c3f84532532c5db966c798bd6

#[derive(Debug)]
pub enum A {
    X(String),
    Y(i32),
}
pub fn f(a: &A) {
    match *a {
        A::X(_s) => {
            //  dbg!(s);
        }
        A::Y(_i) => {
            //  dbg!(i);
        }
    };
}

This fails to compile:

error[E0507]: cannot move out of `a.0` which is behind a shared reference
 --> src/lib.rs:7:11
  |
7 |     match *a {
  |           ^^ help: consider borrowing here: `&*a`
8 |         A::X(_s) => {
  |              --
  |              |
  |              data moved here
  |              move occurs because `_s` has type `std::string::String`, which does not implement the `Copy` trait

So it appears that dereferencing a non-Copy enum is perfectly fine, as long as it wouldn't be used for moving inner non-Copy values. _ is fine because it guarantees that the underlying value would never be used, while _s does not compile because it is just a normal allow(unused) variable.

This also makes sense because it allows the same match arm to work on both Copy and non-Copy types, as long as no usages violate the ownership rules

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM