Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I directly match an array of Options but not a variable containing an array of Options?

Tags:

rust

The following code compiles:

fn consume(_: Box<u64>) {}
let tuple = (Some(Box::new(1)), Some(Box::new(2)));
match tuple {
    (Some(x), Some(y)) => {
        consume(x);
        consume(y);
    }
    _ => (),
}

The following code compiles:

fn consume(_: Box<u64>) {}
match [Some(Box::new(1)), Some(Box::new(2))] {
    [Some(x), Some(y)] => {
        consume(x);
        consume(y);
    }
    _ => (),
}

But this code does not compile:

fn consume(_: Box<u64>) {}
let array = [Some(Box::new(1)), Some(Box::new(2))];
match array {
    [Some(x), Some(y)] => {
        consume(x);
        consume(y);
    }
    _ => (),
}

The compiler says:

error[E0382]: use of moved value: `(array[..] as std::prelude::v1::Some).0`
 --> src/main.rs:5:24
  |
5 |         [Some(x), Some(y)] => {
  |               -        ^ value used here after move
  |               |
  |               value moved here
  |
  = note: move occurs because the value has type `std::boxed::Box<u64>`, which does not implement the `Copy` trait

Why do the first and second version compile, but not the third version?

like image 722
xardas Avatar asked Dec 03 '25 10:12

xardas


1 Answers

Here's a reduced version of your code:

struct NonCopy;

fn main() {
    // OK
    let tuple = (Some(NonCopy), Some(NonCopy));
    if let (Some(_x), Some(_y)) = tuple {}

    // OK
    if let [Some(_x), Some(_y)] = [Some(NonCopy), Some(NonCopy)] {}

    // Fails
    let array = [Some(NonCopy), Some(NonCopy)];
    if let [Some(_x), Some(_y)] = array {}
}

The good news

This code works as-is when non-lexical lifetimes are enabled.

The bad news

Non-lexical lifetimes aren't stable yet.

The workaround

Explicitly transfer ownership of the array to the match or if let head expression:

let array = [Some(NonCopy), Some(NonCopy)];
if let [Some(_x), Some(_y)] = { array } {}

The explanation

The current implementation of the borrow checker is AST-based while a future implementation will be MIR-based. At a high level, you can think of this as "working on the code as I typed it" (AST) and "working on the logical flow of data in my code" (MIR).

Certain "hacks" have been added to the AST borrow checker, which is why you can successfully use an array literal but not the variable. With the MIR borrow checker, bigger hacks like these will disappear and the borrow checker will also become more precise, allowing more code to compile.

like image 93
Shepmaster Avatar answered Dec 07 '25 18:12

Shepmaster



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!