Skip to content

Instantly share code, notes, and snippets.

@Amjad50
Last active August 30, 2020 15:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Amjad50/ff0a1b279ecb48e50626ebe0bc77e388 to your computer and use it in GitHub Desktop.
Save Amjad50/ff0a1b279ecb48e50626ebe0bc77e388 to your computer and use it in GitHub Desktop.
feature(move_ref_pattern) stabilization

Implementation

  • Initially the rule was added in the run-up to 1.0. The AST-based borrow checker was having difficulty correctly enforcing match expressions that combined ref and move bindings, and so it was decided to simplify forbid the combination out right.
  • The move to MIR-based borrow checking made it possible to enforce the rules in a finer-grained level, but we kept the rule in place in an effort to be conservative in our changes.
  • In #68376, @Centril lifted the restriction but required a feature-gate.
  • his PR removes the feature-gate.

Tracking issue: #68354.

Description

This PR is to stabilize the feature move_ref_pattern, which allows patterns containing both by-ref and by-move bindings at the same time.

For example: Foo(ref x, y), where x is by-ref, and y is by-move.

The rules of moving a variable also apply here when moving part of a variable, such as it can't be referenced or moved before.

If this pattern is used, it would result in partial move, which means that part of the variable is moved. The variable that was partially moved from cannot be used as a whole in this case, only the parts that are still not moved can be used.

Documentation

Tests

There are many tests, but I think one of the comperhensive ones:

Examples

#[derive(PartialEq, Eq)]
struct Finished {}

#[derive(PartialEq, Eq)]
struct Processing {
    status: ProcessStatus,
}

#[derive(PartialEq, Eq)]
enum ProcessStatus {
    One,
    Two,
    Three,
}

#[derive(PartialEq, Eq)]
enum Status {
    Finished(Finished),
    Processing(Processing),
}

fn check_result(_url: &str) -> Status {
    // fetch status from some server
    Status::Processing(Processing {
        status: ProcessStatus::One,
    })
}

fn wait_for_result(url: &str) -> Finished {
    let mut previous_status = None;
    loop {
        match check_result(url) {
            Status::Finished(f) => return f,
            Status::Processing(p) => {
                match (&mut previous_status, p.status) {
                    (None, status) => previous_status = Some(status), // first status
                    (Some(previous), status) if *previous == status => {} // no change, ignore
                    (Some(previous), status) => { // Now it can be used
                        // new status
                        *previous = status;
                    }
                }
            }
        }
    }
}

Before, we would have used:

                match (&previous_status, p.status) {
                    (Some(previous), status) if *previous == status => {} // no change, ignore
                    (_, status) => {
                        // new status
                        previous_status = Some(status);
                    }
                }

Demonstrating partial move

fn main() {
    #[derive(Debug)]
    struct Person {
        name: String,
        age: u8,
    }

    let person = Person {
        name: String::from("Alice"),
        age: 20,
    };

    // `name` is moved out of person, but `age` is referenced
    let Person { name, ref age } = person;

    println!("The person's age is {}", age);

    println!("The person's name is {}", name);

    // Error! borrow of partially moved value: `person` partial move occurs
    //println!("The person struct is {:?}", person);

    // `person` cannot be used but `person.age` can be used as it is not moved
    println!("The person's age from person struct is {}", person.age);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment