From f75d81e4d037327c246d349353dc50ea1e980c30 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Mon, 24 Feb 2025 01:54:52 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E3=80=8C=E3=83=8D=E3=83=90=E3=83=BC?= =?UTF-8?q?=E5=9E=8B=E3=81=AE=E3=83=95=E3=82=A9=E3=83=BC=E3=83=AB=E3=83=90?= =?UTF-8?q?=E3=83=83=E3=82=AF=E5=85=88=E3=81=AE=E5=A4=89=E6=9B=B4=E3=80=8D?= =?UTF-8?q?=E3=82=92=E7=BF=BB=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/never-type-fallback.md | 184 ++++++++++++++++++++++++++- 2 files changed, 183 insertions(+), 3 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 6d03c91..81a963b 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -121,7 +121,7 @@ - [Unsafe attributes](rust-2024/unsafe-attributes.md) - [`unsafe_op_in_unsafe_fn` warning](rust-2024/unsafe-op-in-unsafe-fn.md) - [Disallow references to `static mut`](rust-2024/static-mut-references.md) - - [Never type fallback change](rust-2024/never-type-fallback.md) + - [ネバー型のフォールバック先の変更](rust-2024/never-type-fallback.md) - [Macro fragment specifiers](rust-2024/macro-fragment-specifiers.md) - [Missing macro fragment specifiers](rust-2024/missing-macro-fragment-specifiers.md) - [`gen` keyword](rust-2024/gen-keyword.md) diff --git a/src/rust-2024/never-type-fallback.md b/src/rust-2024/never-type-fallback.md index c600f58..97d91c9 100644 --- a/src/rust-2024/never-type-fallback.md +++ b/src/rust-2024/never-type-fallback.md @@ -1,18 +1,42 @@ -> **Rust Edition Guide は現在 Rust 2024 のアップデート作業に向けて翻訳作業中です。本ページはある時点での英語版をコピーしていますが、一部のリンクが動作しない場合や、最新情報が更新されていない場合があります。問題が発生した場合は、[原文(英語版)](https://doc.rust-lang.org/nightly/edition-guide/introduction.html)をご参照ください。** - + + +# ネバー型のフォールバック先の変更 + + +## 概要 + +- ネバー型(`!`)から任意型への(never-to-any の)型強制は、失敗時にネバー型(`!`)でなくユニット型(`()`)に強制されるようになります。 +- [`never_type_fallback_flowing_into_unsafe`] のリントレベルはデフォルトで `deny` (必ずエラー)となりました。 + + + +[`never_type_fallback_flowing_into_unsafe`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#never-type-fallback-flowing-into-unsafe + +## 詳細 + + + +[型強制サイト][](型強制可能な場所)に `!` (ネバー)と呼ばれる値があるとき、型検査機が任意の型を推測できるように、コンパイラはそこにある種の変換をさし挟みます。 + +```rust,should_panic +# #![feature(never_type)] +// 以下のコードは +let x: u8 = panic!(); + +// コンパイラによって(実質的に)以下のように書き換えられる +let x: u8 = yaba(panic!()); + +// ここで、`yaba` 関数は以下のような関数 +//(`!` は到達不能なコードに対応するので、これは正当なコード) +fn yaba(x: !) -> T { x } +``` + + + +このままだと型推論に失敗したときにコンパイルエラーが発生してしまいます。 + + +```rust,compile_fail,E0282 +# #![feature(never_type)] +# fn yaba(x: !) -> T { x } +// これが +{ panic!() }; +// これになる +{ yaba(panic!()) }; //~ ERROR can't infer the type of `yaba` + //~ (訳) エラー: `yaba` の型が推論できません +``` + + + +このようなエラーを回避するため、コンパイラは `yaba` が挿入された箇所を記録していて、推論に失敗したときにフォールバック(代替)されます。 + + +```rust,should_panic +# #![feature(never_type)] +# fn absurd(x: !) -> T { x } +type Fallback = /* どの型にするかはコンパイラの自由!*/ !; +{ absurd::(panic!()) } +``` + +この現象は、ネバー型のフォールバックと呼ばれます。 + + + +かつてはフォールバック先の型は `()` 型(ユニット型)でした。 +これにより、フォールバックがなければコンパイラが `()` を推論としない場面であっても、`!` が自発的に `()` に強制されてしまいました。 +この動作は非直感的で、`!` 型の安定化も妨げていました。 + + +2024 エディションから、フォールバック先の型が `!` 型になりました。 +(将来的には全エディションでそうする予定です。) +これによりコンパイラの動作がより直感的になります。 +これからは、別の型に強制される特段の理由がない限り、`!` は `!` のままです。 + +`()` へのフォールバックを前提としたコードは、本変更によってコンパイルエラーになったり、挙動が変わったりするおそれがあります。 + + + +[型強制サイト]: https://doc.rust-lang.org/reference/type-coercions.html#coercion-sites ### `never_type_fallback_flowing_into_unsafe` + +2024 エディションでは、[`never_type_fallback_flowing_into_unsafe`] のデフォルトレベルが `warn`(警告)から `deny`(必ずエラー)に格上げされました。 +このリントは、`unsafe` 文脈における `!` へのフォールバックが関わる動作のうち、未定義動作を引き起こす可能性のあるものを検知します。 +詳細説明はリンク先をご参照ください。 + + + +## 移行 + +自動修正はできませんが、エディションの更新によって壊れうるコードを自動検知することはできます。 +また、過去のエディションにおいてはコードが壊れうる場合に警告が出ます。 + + + +修正方法は、フォールバックが起こらないように強制先となるべき型を明示することです。 +残念ながら、どの型を指定すべきかは自明ではありません。 + + +壊れうるコードの中では比較的よく見られるパターンとしては、`f()?;` というコード中で、特に `f` の戻り値の型が `Ok` 側の型引数に対してジェネリックな場合が挙げられます。 ```rust # #![allow(dependency_on_unit_never_type_fallback)] @@ -81,10 +198,19 @@ f()?; # } ``` + + +一見型 `T` は推論不可能に見えますが、`?` 構文の脱糖 (desugaring) によりかつては `()` と、今は `!` と推論されます。 + + +対策として、`T` の型を明示するとよいです。 + + +```rust,edition2024 +# #![deny(dependency_on_unit_never_type_fallback)] +# fn outer(x: T) -> Result { +# fn f() -> Result { +# Ok(T::default()) +# } +f::<()>()?; +// または +() = f()?; +# Ok(x) +# } +``` + + +他のよくある例としては、クロージャ中でパニックする場合が挙げられます。 ```rust,should_panic # #![allow(dependency_on_unit_never_type_fallback)] @@ -112,7 +256,13 @@ fn run(f: impl FnOnce() -> R) { run(|| panic!()); ``` + + +かつては、`panic!` の返す `!` は `Unit` を実装する `()` に強制されていました。 +本エディションからは、`!` が `!` のままなので、`!` が `Unit` を実装しない以上コンパイルに失敗してしまいます。 +このエラーは、クロージャの返り値の型を明示すると解決できます。 ```rust,edition2024,should_panic # #![deny(dependency_on_unit_never_type_fallback)] @@ -127,6 +277,8 @@ run(|| -> () { panic!() }); A similar case to that of `f()?` can be seen when using a `!`-typed expression in one branch and a function with an unconstrained return type in the other: +`f()?` と似た例として、条件分岐等の一方が `!` 型の式で、もう片方が返り値の型が指定されていない関数の呼び出しである場合が挙げられます。 + ```rust # #![allow(dependency_on_unit_never_type_fallback)] if true { @@ -136,10 +288,20 @@ if true { }; ``` + +かつては `return` の型である `!` が誤って `()` に強制されていたため、`Default::default()` の戻り値型として `()` が推論されていました。 +本エディションからは `!` が `!` のままと推論されるようになり、`!` は `Default` を実装しないのでコンパイルエラーになります。 + + + +これも同じく、型を明示することで解決できます。 + + +```rust,edition2024 +# #![deny(dependency_on_unit_never_type_fallback)] +() = if true { + Default::default() +} else { + return +}; + +// または + +if true { + <() as Default>::default() +} else { + return +}; +``` From 7f3350057116fe7d7f2a989328033fe330a4a8a1 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Mon, 24 Feb 2025 19:01:09 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E3=82=BB=E3=83=AB=E3=83=95=E3=83=81?= =?UTF-8?q?=E3=82=A7=E3=83=83=E3=82=AF=E5=AE=8C=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/SUMMARY.md | 2 +- src/rust-2024/never-type-fallback.md | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 81a963b..38d0fca 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -121,7 +121,7 @@ - [Unsafe attributes](rust-2024/unsafe-attributes.md) - [`unsafe_op_in_unsafe_fn` warning](rust-2024/unsafe-op-in-unsafe-fn.md) - [Disallow references to `static mut`](rust-2024/static-mut-references.md) - - [ネバー型のフォールバック先の変更](rust-2024/never-type-fallback.md) + - [never 型のフォールバック先の変更](rust-2024/never-type-fallback.md) - [Macro fragment specifiers](rust-2024/macro-fragment-specifiers.md) - [Missing macro fragment specifiers](rust-2024/missing-macro-fragment-specifiers.md) - [`gen` keyword](rust-2024/gen-keyword.md) diff --git a/src/rust-2024/never-type-fallback.md b/src/rust-2024/never-type-fallback.md index 97d91c9..2f564f5 100644 --- a/src/rust-2024/never-type-fallback.md +++ b/src/rust-2024/never-type-fallback.md @@ -2,7 +2,7 @@ # Never type fallback change --> -# ネバー型のフォールバック先の変更 +# never 型のフォールバック先の変更 -- ネバー型(`!`)から任意型への(never-to-any の)型強制は、失敗時にネバー型(`!`)でなくユニット型(`()`)に強制されるようになります。 +- never 型(`!`)から任意型への(never-to-any の)型強制において、失敗時のフォールバック先が never 型(`!`)でなくユニット型(`()`)となります。 - [`never_type_fallback_flowing_into_unsafe`] のリントレベルはデフォルトで `deny` (必ずエラー)となりました。 -[型強制サイト][](型強制可能な場所)に `!` (ネバー)と呼ばれる値があるとき、型検査機が任意の型を推測できるように、コンパイラはそこにある種の変換をさし挟みます。 +[型強制サイト][](型強制可能な場所)に `!` (never) と呼ばれる値があるとき、型検査機が任意の型を推論できるように、コンパイラはそこにある種の変換をさし挟みます。 -このようなエラーを回避するため、コンパイラは `yaba` が挿入された箇所を記録していて、推論に失敗したときにフォールバック(代替)されます。 +このようなエラーを避けるため、コンパイラには、「`yaba` が挿入された場所で型推論に失敗した場合には、代わりにそれをこの型と見做して処理を続ける」というような「失敗時のデフォルト」用の型(フォールバック先の型)が決まっています。 -この現象は、ネバー型のフォールバックと呼ばれます。 +この現象は、never 型のフォールバックと呼ばれます。 かつてはフォールバック先の型は `()` 型(ユニット型)でした。 -これにより、フォールバックがなければコンパイラが `()` を推論としない場面であっても、`!` が自発的に `()` に強制されてしまいました。 +これにより、フォールバックが発生しなければコンパイラが `()` を推論としないはずの場面であっても、実際には `!` が勝手に `()` に強制されてしまいました。 この動作は非直感的で、`!` 型の安定化も妨げていました。 2024 エディションから、フォールバック先の型が `!` 型になりました。 -(将来的には全エディションでそうする予定です。) +(将来的には全エディションで採用される予定です。) これによりコンパイラの動作がより直感的になります。 これからは、別の型に強制される特段の理由がない限り、`!` は `!` のままです。 @@ -184,7 +184,7 @@ The fix is to specify the type explicitly so that the fallback type is not used. One of the most common patterns broken by this change is using `f()?;` where `f` is generic over the `Ok`-part of the return type: --> -壊れうるコードの中では比較的よく見られるパターンとしては、`f()?;` というコード中で、特に `f` の戻り値の型が `Ok` 側の型引数に対してジェネリックな場合が挙げられます。 +壊れうるコードの中では比較的よく見られるパターンとしては、`f()?;` という式において、特に `f` の戻り値の型が `Ok` 側の型引数に対してジェネリックな場合が挙げられます。 ```rust # #![allow(dependency_on_unit_never_type_fallback)] @@ -202,13 +202,13 @@ f()?; You might think that, in this example, type `T` can't be inferred. However, due to the current desugaring of the `?` operator, it was inferred as `()`, and it will now be inferred as `!`. --> -一見型 `T` は推論不可能に見えますが、`?` 構文の脱糖 (desugaring) によりかつては `()` と、今は `!` と推論されます。 +一見、型 `T` は推論不可能に見えますが、`?` 構文の脱糖 (desugaring) 方法の関係で、かつては `()` と、今は `!` と推論されます。 -対策として、`T` の型を明示するとよいです。 +対策としては、`T` の型を明示するとよいです。 `f()?` と似た例として、条件分岐等の一方が `!` 型の式で、もう片方が返り値の型が指定されていない関数の呼び出しである場合が挙げられます。