Repository: rust
Issue: Desuraring of destructuring assignment has subtle effects on drop-order / scopes of temporaries
Labels: C-enhancement,T-lang,T-compiler,C-bug,F-destructuring_assignment

For example
```rs
fn main() {
    struct L;
    impl Drop for L {
        fn drop(&mut self) {
            println!("dropped L")
        }
    }
    struct M;
    impl Drop for M {
        fn drop(&mut self) {
            println!("dropped M")
        }
    }

    let x;
    (x = (drop(&L), ()), M);

    println!("---"); // -------------

    let (x1, x2);
    ((x1, x2) = (drop(&L), ()), M);
}
```
Output:
```
dropped M
dropped L
---
dropped L
dropped M
```

<hr>

I came across this by reading [the reference](https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#destructuring-assignments):

> Assignee expressions are then desugared to pattern matches followed by sequential assignment.

> The desugaring method is straightforward, and is illustrated best by example.
>
> ```rs
> (a, b) = (3, 4);
>
> [a, b] = [3, 4];
>
> Struct { x: a, y: b } = Struct { x: 3, y: 4};
>
> // desugars to:
>
> {
>     let (_a, _b) = (3, 4);
>     a = _a;
>     b = _b;
> }
>
> {
>     let [_a, _b] = [3, 4];
>     a = _a;
>     b = _b;
> }
>
> {
>     let Struct { x: _a, y: _b } = Struct { x: 3, y: 4};
>     a = _a;
>     b = _b;
> }
>```

The difference between 
```
STATEMENT CONTAINING … x = EXPR …
```
and
```
STATEMENT CONTAINING … { let _x = EXPR; x = _x } …
```
is that in the former case, temporaries in `EXPR` are dropped _after_ the surrounding statement is evaluated, while the latter case has them dropped right after the `let _x = EXPR`.

<hr>

It seems to me that a better desugaring would have been to use something like
```rs
(a, b) = (3, 4);

[a, b] = [3, 4];

Struct { x: a, y: b } = Struct { x: 3, y: 4};

// desugars to:

match (3, 4) {
    (_a, _b) => {
        a = _a;
        b = _b;
    }
}

match [3, 4] {
    [_a, _b] =>
        a = _a;
        b = _b;
    }
}

match Struct { x: 3, y: 4} {
    Struct { x: _a, y: _b } => {
        a = _a;
        b = _b;
    }
}
```

<hr>

Or applied to the example above:
```rs
fn main() {
    ………
    
    let (x1, x2);
    // current desugaring
    (
        {
            let (_x1, _x2) = (drop(&L), ());
            x1 = _x1;
            x2 = _x2;
        },
        M,
    );

    println!("---"); // -------------

    let (x1, x2);
    // better desugaring
    (
        match (drop(&L), ()) {
            (_x1, _x2) => {
                x1 = _x1;
                x2 = _x2;
            }
        },
        M,
    );
}
```
```
dropped L
dropped M
---
dropped M
dropped L
```

[Full example in the playground.](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=666c04b6614b33b686aad9c2b30131e1)

As a point of comparison, e.g. [desugaring of `for` loops](https://doc.rust-lang.org/stable/reference/expressions/loop-expr.html#iterator-loops), and similarly many macros, use `match` in a similar way to ensure proper temporary scopes.

Changing it now might technically be a breaking change, OTOH, the feature of destructuring assignment is still very young, and the change is small. I’m not 100% certain about the "bug" label, but I’ll add it for now, next to "enhancement", to let the reader choose whichever they feel more appropriate: @rustbot label C-bug, C-enhancement, F-destructuring_assignment, T-compiler