From dfca452df5a306fe7b190e6ba73594916e9a8a89 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Tue, 18 Feb 2025 01:49:55 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E3=80=8C=E3=83=9E=E3=83=83=E3=83=81?= =?UTF-8?q?=E3=81=AE=E5=88=A9=E4=BE=BF=E6=80=A7=E6=A9=9F=E8=83=BD=E3=81=B8?= =?UTF-8?q?=E3=81=AE=E6=96=B0=E5=88=B6=E7=B4=84=E3=80=8D=E3=82=92=E7=BF=BB?= =?UTF-8?q?=E8=A8=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/SUMMARY.md | 2 +- src/rust-2024/match-ergonomics.md | 280 ++++++++++++++++++++++++++++++ 2 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 src/rust-2024/match-ergonomics.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ab10822..e29243e 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -117,7 +117,7 @@ - [RPIT lifetime capture rules](rust-2024/rpit-lifetime-capture.md) - [`if let` temporary scope](rust-2024/temporary-if-let-scope.md) - [Tail expression temporary scope](rust-2024/temporary-tail-expr-scope.md) - - [Match ergonomics reservations](rust-2024/match-ergonomics.md) + - [マッチの利便性機能への新制約](rust-2024/match-ergonomics.md) - [Unsafe `extern` blocks](rust-2024/unsafe-extern.md) - [Unsafe attributes](rust-2024/unsafe-attributes.md) - [`unsafe_op_in_unsafe_fn` warning](rust-2024/unsafe-op-in-unsafe-fn.md) diff --git a/src/rust-2024/match-ergonomics.md b/src/rust-2024/match-ergonomics.md new file mode 100644 index 0000000..debafbf --- /dev/null +++ b/src/rust-2024/match-ergonomics.md @@ -0,0 +1,280 @@ + + +# マッチの利便性機能への新制約 + + + +## 概要 + + + +- パターン中の `mut`、`ref`、`ref mut` 束縛は、その束縛に至るまでのパターンが完全であるとき(マッチの利便性機能を使っていないとき)だけ使用できます。 + - つまり、現在の束縛モードが `move` 以外のとき、`mut`、`ref`、`ref mut` 束縛の使用はエラーになります。 +- 参照パターン (`&` や `&mut`) は、その束縛に至るまでのパターンが完全であるときだけ使用できます。 + - つまり、参照パターンが被検査子中の参照にマッチできるのは、現在の束縛モードが `move` であるときのみです。 + +## Details + +### Background + + + +`match` や `let` などの束縛構文は、以下のように、評価対象の式である **被検査子** が、**パターン** にマッチさせる(合致するかどうか照合する)という構造になっています。 + + + +```rust +let &[&mut [ref x]] = &[&mut [()]]; // x: &() +// ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ +// パターン 被検査子 +``` + + + +上記のようなパターンは、被検査子中の全参照を明示的に記述していることから、「完全な」パターンと呼びます。 +一方で、以下のパターンは上記と等価ながら完全ではありません。 + +```rust +let [[x]] = &[&mut [()]]; // x: &() +``` + + + +このようなパターンは、元は [RFC 2005][] で導入された「マッチの利便性機能」を利用しています。 + + + +この利便性機能において、コンパイラは、被検査子をパターンに逐次マッチさせながら、「現在の束縛モード」を状態として持ちます。 +束縛モードは最初 `move` で、`move`、`ref mut`、`ref` のいずれかをとります。 +束縛に到達したとき、明示的な束縛モードの上書きがない限り、現在の束縛モードにより束縛方式が決まります。 + + + +以下は明示的な束縛モードを指定して、`x` を参照束縛にしている例です。 + +```rust +let ref x = (); // &() +``` + + + +一方で、 + +```rust +let [x] = &[()]; // &() +``` + + + +こちらでは、被検査子中の外部の共有参照をパターン中で引き渡しています。 +これにより現在の束縛モードが `move` から `ref` に切り替わります。 +束縛モードが明示されていないので、`ref` モードが `x` の束縛に使われます。 + +[RFC 2005]: https://github.com/rust-lang/rfcs/pull/2005 + + + +### `mut` を書ける場所への制限 + + + +Rust 2021 以前のエディションでは、以下の不思議な挙動が仕様でした。 + +```rust,edition2021 +let [x, mut y] = &[(), ()]; // x: &(), mut y: () +``` + + + +パターンに共有参照を渡した時点では、現在の束縛モードが `ref` に切り替わっていますが、Rust 2021 以前では、束縛に `mut` を書くことで現在の束縛モードが `move` に戻ってしまいます。 + + + +変数を可変にするかどうかで型が変わってしまいました。非直感的な挙動ですね。 + + + +Rust 2024 では、将来的にこの問題を修正するために、現在の束縛モードが `move` でないときに `mut` を束縛中に書くことはエラーになりました。 +つまり、`mut` 束縛が書けるのは(そこに至るまでの)パターンが完全であるときだけです。 + + + +Rust 2024 では、上記のコードは以下のように書けます。 + +```rust +let &[ref x, mut y] = &[(), ()]; // x: &(), mut y: () +``` + +### `ref` / `ref mut` restriction + + + +Rust 2021 以前のエディションでは、以下のように書けました。 + +```rust,edition2021 +let [ref x] = &[()]; // x: &() +``` + + + +ここで、`ref` 束縛モードを明示するのは冗長です。共有参照を渡した時点で(それがパターン側で言及されていないため)、束縛モードが `ref` に変わっているからです。 + + + +今後の言語仕様の行く末を見越して、Rust 2024 では冗長な束縛モードの指定がエラーになります。 +実際、上記のコードは以下で十分です。 + +```rust +let [x] = &[()]; // x: &() +``` + +### Reference patterns restriction + + + +Rust 2021 以前のエディションでは、以下の不思議な挙動が仕様でした。 + +```rust,edition2021 +let [&x, y] = &[&(), &()]; // x: (), y: &&() +``` + + + +ここで、パターン中の `&` には、`&()` にマッチした上で、現在の束縛モードを `move` に切り替える効果がありました。 +一つ `&` を書いただけで、参照が二重に剥かれて、思いもよらぬ型になってしまいました。非直感的な挙動ですね。 + + + +将来的にこの問題を修正するために、Rust 2024 では、現在の束縛モードが `move` でないときに `&` や `&mut` をパターン中に書くのはエラーになりました。 +逆に、`&` と `&mut` が書けるのは、(そこに至るまでの)パターンが完全であるときだけです。 + + + +Rust 2024 では、上記のコードは以下のように書けます。 + +```rust +let &[&x, ref y] = &[&(), &()]; +``` + + + +## 移行 + + + +[`rust_2024_incompatible_pat`][] リントで、Rust 2024 で新たに禁止されるパターンを検出できます。 +`cargo fix --edition` を実行すると自動適用される `rust-2024-compatibility` リントグループの一部です。 +このリントは、影響を受けるパターンを、Rust 2024 を含む全エディションで正しく動作するパターンに書き換えます。 + + + +コードを Rust 2024 互換に移行するには、以下を実行します。 + +```sh +cargo fix --edition +``` + + + +これにより、以下のコードは + +```rust,edition2021 +let [x, mut y] = &[(), ()]; +let [ref x] = &[()]; +let [&x, y] = &[&(), &()]; +``` + + + +以下のコードに変換されます。 + +```rust +let &[ref x, mut y] = &[(), ()]; +let &[ref x] = &[()]; +let &[&x, ref y] = &[&(), &()]; +``` + + + +あるいは、移行が必要なパターンを検出するために手動でリンクを有効化できます。 + + + +```rust +// クレートのトップレベルに以下を追加すると手動移行できる +#![warn(rust_2024_incompatible_pat)] +``` + +[`rust_2024_incompatible_pat`]: ../../rustc/lints/listing/allowed-by-default.html#rust-2024-incompatible-pat From f85decdda51195cda9ff7de65c50e5f8447f2872 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Wed, 19 Feb 2025 00:34:21 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E7=BF=BB=E8=A8=B3=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rust-2024/match-ergonomics.md | 34 +++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/rust-2024/match-ergonomics.md b/src/rust-2024/match-ergonomics.md index debafbf..81d5018 100644 --- a/src/rust-2024/match-ergonomics.md +++ b/src/rust-2024/match-ergonomics.md @@ -22,15 +22,23 @@ - 参照パターン (`&` や `&mut`) は、その束縛に至るまでのパターンが完全であるときだけ使用できます。 - つまり、参照パターンが被検査子中の参照にマッチできるのは、現在の束縛モードが `move` であるときのみです。 + + +## 詳細 + + +### 背景 -`match` や `let` などの束縛構文は、以下のように、評価対象の式である **被検査子** が、**パターン** にマッチさせる(合致するかどうか照合する)という構造になっています。 +`match` や `let` などの束縛構文は、以下のように、評価対象の式である **被検査子** を **パターン** にマッチさせる(合致するかどうか照合する)という構造になっています。 -上記のようなパターンは、被検査子中の全参照を明示的に記述していることから、「完全な」パターンと呼びます。 +上記のようなパターンは、被検査子中の全ての参照を明示的に記述していることから、「完全な」パターンと呼びます。 一方で、以下のパターンは上記と等価ながら完全ではありません。 ```rust @@ -61,7 +69,7 @@ let [[x]] = &[&mut [()]]; // x: &() Patterns such as this are said to be using match ergonomics, originally introduced in [RFC 2005][]. --> -このようなパターンは、元は [RFC 2005][] で導入された「マッチの利便性機能」を利用しています。 +このようなパターンは、[RFC 2005][] で最初に導入された「マッチの利便性機能」を利用しています。 -パターンに共有参照を渡した時点では、現在の束縛モードが `ref` に切り替わっていますが、Rust 2021 以前では、束縛に `mut` を書くことで現在の束縛モードが `move` に戻ってしまいます。 +パターンに共有参照を渡した時点で、現在の束縛モードが `ref` に切り替わっていますが、Rust 2021 以前では、束縛に `mut` を書くことで現在の束縛モードが `move` に戻ります。 -変数を可変にするかどうかで型が変わってしまいました。非直感的な挙動ですね。 +これにより、変数を可変にするかどうかで型が変わってしまいます。非直感的な挙動ですね。 + +### `ref` と `ref mut` への制限 -今後の言語仕様の行く末を見越して、Rust 2024 では冗長な束縛モードの指定がエラーになります。 +今後の仕様変更の余地を残すため、Rust 2024 では冗長な束縛モードの指定がエラーになります。 実際、上記のコードは以下で十分です。 ```rust let [x] = &[()]; // x: &() ``` + + +### 参照パターンへの制限 -ここで、パターン中の `&` には、`&()` にマッチした上で、現在の束縛モードを `move` に切り替える効果がありました。 +ここで、パターン中の `&` には、`&()` にマッチした上で、現在の束縛モードを `move` に切り替える効果があります。 一つ `&` を書いただけで、参照が二重に剥かれて、思いもよらぬ型になってしまいました。非直感的な挙動ですね。