diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 6d03c91..81b38fa 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -116,7 +116,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 index b96c9d4..81d5018 100644 --- a/src/rust-2024/match-ergonomics.md +++ b/src/rust-2024/match-ergonomics.md @@ -1,117 +1,261 @@ -> **Rust Edition Guide は現在 Rust 2024 のアップデート作業に向けて翻訳作業中です。本ページはある時点での英語版をコピーしていますが、一部のリンクが動作しない場合や、最新情報が更新されていない場合があります。問題が発生した場合は、[原文(英語版)](https://doc.rust-lang.org/nightly/edition-guide/introduction.html)をご参照ください。** - + + +# マッチの利便性機能への新制約 + + +## 概要 + +- パターン中の `mut`、`ref`、`ref mut` 束縛は、その束縛に至るまでのパターンが完全であるとき(マッチの利便性機能を使っていないとき)だけ使用できます。 + - つまり、現在の束縛モードが `move` 以外のとき、`mut`、`ref`、`ref mut` 束縛の使用はエラーになります。 +- 参照パターン (`&` や `&mut`) は、その束縛に至るまでのパターンが完全であるときだけ使用できます。 + - つまり、参照パターンが被検査子中の参照にマッチできるのは、現在の束縛モードが `move` であるときのみです。 + + + +## 詳細 + + +### 背景 + +`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` に切り替わります。 +束縛モードが明示されていないので、`x` の束縛には `ref` モードが使われます。 [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` への制限 + + +Rust 2021 以前のエディションでは、以下のように書けました。 ```rust,edition2021 let [ref x] = &[()]; // x: &() ``` + + +ここで、`ref` 束縛モードを明示するのは冗長です。共有参照を渡した時点で(それがパターン側で言及されていないため)、束縛モードが `ref` に変わっているからです。 + + +今後の仕様変更の余地を残すため、Rust 2024 では冗長な束縛モードの指定がエラーになります。 +実際、上記のコードは以下で十分です。 ```rust let [x] = &[()]; // x: &() ``` + +### 参照パターンへの制限 + + + +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] = &[(), ()]; @@ -119,7 +263,11 @@ let [ref x] = &[()]; let [&x, y] = &[&(), &()]; ``` + + +以下のコードに変換されます。 ```rust let &[ref x, mut y] = &[(), ()]; @@ -127,11 +275,22 @@ 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