From d944d08cf33a2659894f8fb7413f1ed67ac09043 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Tue, 28 Jan 2025 05:31:12 +0900 Subject: [PATCH 1/8] =?UTF-8?q?=E7=9B=AE=E6=AC=A1=E3=83=9A=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=82=92=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/SUMMARY.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 5c12430..ab10822 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -1,7 +1,7 @@ -# エディションガイド +# Rust エディションガイド - [Rust 2021](rust-2021/index.md) @@ -68,5 +70,77 @@ - [クロージャはフィールドごとにキャプチャする](rust-2021/disjoint-capture-in-closures.md) - [panic マクロの一貫性](rust-2021/panic-macro-consistency.md) - [構文の予約](rust-2021/reserving-syntax.md) + - [Raw lifetimes](rust-2021/raw-lifetimes.md) - [警告からエラーへの格上げ](rust-2021/warnings-promoted-to-error.md) - [マクロ規則における OR パターン](rust-2021/or-patterns-macro-rules.md) + - [C-string literals](rust-2021/c-string-literals.md) + +## Rust 2024 + + + +- [Rust 2024](rust-2024/index.md) + - [Language](rust-2024/language.md) + - [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) + - [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) + - [Disallow references to `static mut`](rust-2024/static-mut-references.md) + - [Never type fallback change](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) + - [Reserved syntax](rust-2024/reserved-syntax.md) + - [Standard library](rust-2024/standard-library.md) + - [Changes to the prelude](rust-2024/prelude.md) + - [Add `IntoIterator` for `Box<[T]>`](rust-2024/intoiterator-box-slice.md) + - [Newly unsafe functions](rust-2024/newly-unsafe-functions.md) + - [Cargo](rust-2024/cargo.md) + - [Cargo: Rust-version aware resolver](rust-2024/cargo-resolver.md) + - [Cargo: Table and key name consistency](rust-2024/cargo-table-key-names.md) + - [Cargo: Reject unused inherited default-features](rust-2024/cargo-inherited-default-features.md) + - [Rustdoc](rust-2024/rustdoc.md) + - [Rustdoc combined tests](rust-2024/rustdoc-doctests.md) + - [Rustdoc nested `include!` change](rust-2024/rustdoc-nested-includes.md) + - [Rustfmt](rust-2024/rustfmt.md) + - [Rustfmt: Style edition](rust-2024/rustfmt-style-edition.md) + - [Rustfmt: Formatting fixes](rust-2024/rustfmt-formatting-fixes.md) + - [Rustfmt: Combine all delimited exprs as last argument](rust-2024/rustfmt-overflow-delimited-expr.md) + - [Rustfmt: Raw identifier sorting](rust-2024/rustfmt-raw-identifier-sorting.md) + - [Rustfmt: Version sorting](rust-2024/rustfmt-version-sorting.md) From 5f9145a6bf2e6f93f2a18b698884cab89fcfc892 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Mon, 3 Feb 2025 08:41:51 +0900 Subject: [PATCH 2/8] =?UTF-8?q?RPIT=20lifetime=20capture=20=E3=82=92?= =?UTF-8?q?=E9=80=94=E4=B8=AD=E3=81=BE=E3=81=A7=E7=BF=BB=E8=A8=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rust-2024/rpit-lifetime-capture.md | 401 +++++++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 src/rust-2024/rpit-lifetime-capture.md diff --git a/src/rust-2024/rpit-lifetime-capture.md b/src/rust-2024/rpit-lifetime-capture.md new file mode 100644 index 0000000..7679c72 --- /dev/null +++ b/src/rust-2024/rpit-lifetime-capture.md @@ -0,0 +1,401 @@ + + +# RPIT におけるライフタイムキャプチャ規則 + + + +本節では、[RFC 3498] で導入された**ライフタイムキャプチャ規則 2024** に関する変更事項を説明します。 +また、([RFC 3617] で導入された)不透明型への**精密なキャプチャ**を使ったコードへの移行方法も説明します。 + +[RFC 3498]: https://github.com/rust-lang/rfcs/pull/3498 +[RFC 3617]: https://github.com/rust-lang/rfcs/pull/3617 + + + +## 概要 + + + +- Rust 2024 では、`use<..>` を書かない限り、スコープ内の **すべての** ジェネリックパラメータ(ライフタイムパラメータを含む)が暗黙にキャプチャされます。 +- `Captures` パターン(`Captures<..>` 境界)や「長生き」パターン(`'_` 境界)は、(どのエディションでも)`use<..>` 境界に書き換えたり、(Rust 2024 では)削除したりできます。 + + + +## 詳細 + + + +### キャプチャ + + + +戻り値としての `impl Trait` (return-position impl Trait, 以下 RPIT) 不透明型が「ジェネリックパラメータをキャプチャする」とは、対応する隠された型がそのパラメータを使用できる、ということです。 +Rust 1.82 で導入された `use<..>` 境界を用いると、どのジェネリックパラメータがキャプチャされているのかを明示することができるようになりました。 +この構文は Rust 2024 への移行にも活用できますし、本節では各エディションにおける暗黙のキャプチャ規則を説明するためにも用います。 +`use<..>` 境界は次のように使用します。 + + + +```rust +# #![feature(precise_capturing)] +fn capture<'a, T>(x: &'a (), y: T) -> impl Sized + use<'a, T> { + // ~~~~~~~~~~~~~~~~~~~~~~~ + // これが RPIT 不透明型です。 + // + // `'a` と `T` をキャプチャしています。 + (x, y) + //~~~~~~ + // 型 `(&'a (), T)` が隠蔽されています。 + // + // この型が `'a` と `T` を使えるのは、これらがキャプチャされているからです。 +} +``` + + + +キャプチャされるジェネリックパラメータによって、不透明型が使える範囲も決まります。 +例えば、以下のコードではライフタイム `'a` が使用されていないにもかかわらず、`'a` がキャプチャされているためエラーになります。 + +```rust,compile_fail +# #![feature(precise_capturing)] +fn capture<'a>(_: &'a ()) -> impl Sized + use<'a> {} + +fn test<'a>(x: &'a ()) -> impl Sized + 'static { + capture(x) + //~^ ERROR lifetime may not live long enough + // エラー: ライフタイムが十分に長生きしません +} +``` + + + +一方で、こうすれば大丈夫です。 + +```rust +# #![feature(precise_capturing)] +fn capture<'a>(_: &'a ()) -> impl Sized + use<> {} + +fn test<'a>(x: &'a ()) -> impl Sized + 'static { + capture(x) //~ OK +} +``` + + + +### `use<..>` がない場合のエディションごとの挙動の違い + + + +`use<..>` が明示されていない場合、スコープ内のジェネリックパラメータのうちどれをキャプチャするかは、エディションによって挙動が異なります。 + + + +スコープ内のジェネリックな型パラメータと const パラメータは、`use<..>` が明示されていないときはエディションにかかわらず全て暗黙にキャプチャされます。 + + + +```rust +# #![feature(precise_capturing)] +fn f_implicit() -> impl Sized {} +// ~~~~~~~~~~ +// `use<..>` が明示されていません。 +// +// どのエディションでも、上記は以下と等価です。 +fn f_explicit() -> impl Sized + use {} +``` + + + +ジェネリックなライフタイムパラメータに関しては、Rust 2021 以前のエディションでは、`use<..>` が明示されていない場合、トップレベルの関数か固有 impl 内の関連関数・メソッドにおける RPIT 不透明型の境界に現れるものだけがキャプチャされます。一方、Rust 2024 以降では、スコープ内のジェネリックなライフタイムパラメータは全てキャプチャされます。例えば以下の通りです。 + + + +```rust +# #![feature(precise_capturing)] +fn f_implicit(_: &()) -> impl Sized {} +// Rust 2021 以前では、上記の定義は以下と等価です。 +fn f_2021(_: &()) -> impl Sized + use<> {} +// Rust 2024 以降では、上記の定義は以下と等価です。 +fn f_2024(_: &()) -> impl Sized + use<'_> {} +``` + + + +これは、他のシグネチャにおける RPIT 不透明型の挙動に合わせたものです。 +実際、トレイト impl における関連関数・メソッド、トレイト定義内の RPIT(Return-Position Impl Trait In Trait; RPITIT)、`asnyc fn` によって生成される不透明な `Future` 型は、 +スコープ内のジェネリックなライフタイムパラメータをエディションにかかわらず全て暗黙にキャプチャするようになっていました。 + +### Outer generic parameters + +Generic parameters from an outer impl are considered to be in scope when deciding what is implicitly captured. E.g.: + +```rust +# #![feature(precise_capturing)] +struct S((T, [(); C])); +impl S { +// ~~~~~~~~~~~~~~~~~ +// These generic parameters are in scope. + fn f_implicit() -> impl Sized {} + // ~ ~~~~~~~~~~ + // ^ This generic is in scope too. + // ^ + // | + // No `use<..>` bound is present here. + // + // In all editions, it's equivalent to: + fn f_explicit() -> impl Sized + use {} +} +``` + +### Lifetimes from higher-ranked binders + +Similarly, generic lifetime parameters introduced into scope by a higher-ranked `for<..>` binder are considered to be in scope. E.g.: + +```rust +# #![feature(precise_capturing)] +trait Tr<'a> { type Ty; } +impl Tr<'_> for () { type Ty = (); } + +fn f_implicit() -> impl for<'a> Tr<'a, Ty = impl Copy> {} +// In Rust 2021 and earlier, the above is equivalent to: +fn f_2021() -> impl for<'a> Tr<'a, Ty = impl Copy + use<>> {} +// In Rust 2024 and later, it's equivalent to: +//fn f_2024() -> impl for<'a> Tr<'a, Ty = impl Copy + use<'a>> {} +// ~~~~~~~~~~~~~~~~~~~~ +// However, note that the capturing of higher-ranked lifetimes in +// nested opaque types is not yet supported. +``` + +### Argument position impl Trait (APIT) + +Anonymous (i.e. unnamed) generic parameters created by the use of APIT (argument position impl Trait) are considered to be in scope. E.g.: + +```rust +# #![feature(precise_capturing)] +fn f_implicit(_: impl Sized) -> impl Sized {} +// ~~~~~~~~~~ +// This is called APIT. +// +// The above is *roughly* equivalent to: +fn f_explicit<_0: Sized>(_: _0) -> impl Sized + use<_0> {} +``` + +Note that the former is not *exactly* equivalent to the latter because, by naming the generic parameter, turbofish syntax can now be used to provide an argument for it. There is no way to explicitly include an anonymous generic parameter in a `use<..>` bound other than by converting it to a named generic parameter. + +## Migration + +### Migrating while avoiding overcapturing + +The `impl_trait_overcaptures` lint flags RPIT opaque types that will capture additional lifetimes in Rust 2024. This lint is part of the `rust-2024-compatibility` lint group which is automatically applied when running `cargo fix --edition`. In most cases, the lint can automatically insert `use<..>` bounds where needed such that no additional lifetimes are captured in Rust 2024. + +To migrate your code to be compatible with Rust 2024, run: + +```sh +cargo fix --edition +``` + +For example, this will change: + +```rust +fn f<'a>(x: &'a ()) -> impl Sized { *x } +``` + +...into: + +```rust +# #![feature(precise_capturing)] +fn f<'a>(x: &'a ()) -> impl Sized + use<> { *x } +``` + +Without this `use<>` bound, in Rust 2024, the opaque type would capture the `'a` lifetime parameter. By adding this bound, the migration lint preserves the existing semantics. + +### Migrating cases involving APIT + +In some cases, the lint cannot make the change automatically because a generic parameter needs to be given a name so that it can appear within a `use<..>` bound. In these cases, the lint will alert you that a change may need to be made manually. E.g., given: + +```rust,edition2021 +fn f<'a>(x: &'a (), y: impl Sized) -> impl Sized { (*x, y) } +// ^^ ~~~~~~~~~~ +// This is a use of APIT. +// +//~^ WARN `impl Sized` will capture more lifetimes than possibly intended in edition 2024 +//~| NOTE specifically, this lifetime is in scope but not mentioned in the type's bounds +# +# fn test<'a>(x: &'a (), y: ()) -> impl Sized + 'static { +# f(x, y) +# } +``` + +The code cannot be converted automatically because of the use of APIT and the fact that the generic type parameter must be named in the `use<..>` bound. To convert this code to Rust 2024 without capturing the lifetime, you must name that type parameter. E.g.: + +```rust +# #![feature(precise_capturing)] +# #![deny(impl_trait_overcaptures)] +fn f<'a, T: Sized>(x: &'a (), y: T) -> impl Sized + use { (*x, y) } +// ~~~~~~~~ +// The type parameter has been named here. +# +# fn test<'a>(x: &'a (), y: ()) -> impl Sized + use<> { +# f(x, y) +# } +``` + +Note that this changes the API of the function slightly as a type argument can now be explicitly provided for this parameter using turbofish syntax. If this is undesired, you might consider instead whether you can simply continue to omit the `use<..>` bound and allow the lifetime to be captured. This might be particularly desirable if you might in the future want to use that lifetime in the hidden type and would like to save space for that. + +### Migrating away from the `Captures` trick + +Prior to the introduction of precise capturing `use<..>` bounds in Rust 1.82, correctly capturing a lifetime in an RPIT opaque type often required using the `Captures` trick. E.g.: + +```rust +#[doc(hidden)] +pub trait Captures {} +impl Captures for U {} + +fn f<'a, T>(x: &'a (), y: T) -> impl Sized + Captures<(&'a (), T)> { +// ~~~~~~~~~~~~~~~~~~~~~ +// This is called the `Captures` trick. + (x, y) +} +# +# fn test<'t, 'x>(t: &'t (), x: &'x ()) { +# f(t, x); +# } +``` + +With the `use<..>` bound syntax, the `Captures` trick is no longer needed and can be replaced with the following in all editions: + +```rust +# #![feature(precise_capturing)] +fn f<'a, T>(x: &'a (), y: T) -> impl Sized + use<'a, T> { + (x, y) +} +# +# fn test<'t, 'x>(t: &'t (), x: &'x ()) { +# f(t, x); +# } +``` + +In Rust 2024, the `use<..>` bound can often be omitted entirely, and the above can be written simply as: + +```rust,edition2024 +# #![feature(lifetime_capture_rules_2024)] +fn f<'a, T>(x: &'a (), y: T) -> impl Sized { + (x, y) +} +# +# fn test<'t, 'x>(t: &'t (), x: &'x ()) { +# f(t, x); +# } +``` + +There is no automatic migration for this, and the `Captures` trick still works in Rust 2024, but you might want to consider migrating code manually away from using this old trick. + +### Migrating away from the outlives trick + +Prior to the introduction of precise capturing `use<..>` bounds in Rust 1.82, it was common to use the "outlives trick" when a lifetime needed to be used in the hidden type of some opaque. E.g.: + +```rust +fn f<'a, T: 'a>(x: &'a (), y: T) -> impl Sized + 'a { + // ~~~~ ~~~~ + // ^ This is the outlives trick. + // | + // This bound is needed only for the trick. + (x, y) +// ~~~~~~ +// The hidden type is `(&'a (), T)`. +} +``` + +This trick was less baroque than the `Captures` trick, but also less correct. As we can see in the example above, even though any lifetime components within `T` are independent from the lifetime `'a`, we're required to add a `T: 'a` bound in order to make the trick work. This created undue and surprising restrictions on callers. + +Using precise capturing, you can write the above instead, in all editions, as: + +```rust +# #![feature(precise_capturing)] +fn f(x: &(), y: T) -> impl Sized + use<'_, T> { + (x, y) +} +# +# fn test<'t, 'x>(t: &'t (), x: &'x ()) { +# f(t, x); +# } +``` + +In Rust 2024, the `use<..>` bound can often be omitted entirely, and the above can be written simply as: + +```rust,edition2024 +# #![feature(precise_capturing)] +# #![feature(lifetime_capture_rules_2024)] +fn f(x: &(), y: T) -> impl Sized { + (x, y) +} +# +# fn test<'t, 'x>(t: &'t (), x: &'x ()) { +# f(t, x); +# } +``` + +There is no automatic migration for this, and the outlives trick still works in Rust 2024, but you might want to consider migrating code manually away from using this old trick. From b71183722cc8886d82671f6e10be3ee588778759 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Tue, 4 Feb 2025 00:28:11 +0900 Subject: [PATCH 3/8] =?UTF-8?q?RIPT=20lifetime=20capture=20=E3=81=AE?= =?UTF-8?q?=E3=80=8C=E8=A9=B3=E7=B4=B0=E3=80=8D=E3=82=92=E7=BF=BB=E8=A8=B3?= =?UTF-8?q?=E5=AE=8C=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rust-2024/rpit-lifetime-capture.md | 81 ++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/rust-2024/rpit-lifetime-capture.md b/src/rust-2024/rpit-lifetime-capture.md index 7679c72..49370a8 100644 --- a/src/rust-2024/rpit-lifetime-capture.md +++ b/src/rust-2024/rpit-lifetime-capture.md @@ -188,10 +188,20 @@ This makes the behavior consistent with RPIT opaque types in the signature of as 実際、トレイト impl における関連関数・メソッド、トレイト定義内の RPIT(Return-Position Impl Trait In Trait; RPITIT)、`asnyc fn` によって生成される不透明な `Future` 型は、 スコープ内のジェネリックなライフタイムパラメータをエディションにかかわらず全て暗黙にキャプチャするようになっていました。 + +### 外側のジェネリックパラメータ + + + +暗黙にキャプチャされるジェネリックパラメータを決定するとき、外側の impl で定義されたパラメータはスコープ内のパラメータと見なされます。 +例えば以下の通りです。 + + +```rust +# #![feature(precise_capturing)] +struct S((T, [(); C])); +impl S { +// ~~~~~~~~~~~~~~~~~ +// これらのジェネリックパラメータはスコープ内にあります。 + fn f_implicit() -> impl Sized {} + // ~ ~~~~~~~~~~ + // ^ これもスコープ内です。 + // ^ + // | + // `use<..>` 境界が明示されていません。 + // + // どのエディションでも、これは以下と等価です。 + fn f_explicit() -> impl Sized + use {} +} +``` + + +### 高階束縛のライフタイム + +同様に、高階の `for<..>` 束縛で定義されたライフタイムパラメータも、スコープ内のパラメータと見なされます。 +例えば以下の通りです。 + + + +```rust +# #![feature(precise_capturing)] +trait Tr<'a> { type Ty; } +impl Tr<'_> for () { type Ty = (); } + +fn f_implicit() -> impl for<'a> Tr<'a, Ty = impl Copy> {} +// Rust 2021 以前では、上記は以下と等価です。 +fn f_2021() -> impl for<'a> Tr<'a, Ty = impl Copy + use<>> {} +// Rust 2024 以降では、上記は以下と等価です。 +//fn f_2024() -> impl for<'a> Tr<'a, Ty = impl Copy + use<'a>> {} +// ~~~~~~~~~~~~~~~~~~~~ +// ただし、ネストされた不透明型における高階ライフタイムのキャプチャは +// まだサポート外であることにご注意ください。 +``` + + +### 引数としての impl Trait (APIT) + + +引数としての impl Trait (argument position impl Trait, 以下 APIT) で作られる匿名(無名)ジェネリックパラメータは、スコープ内のパラメータと見なされます。 +例えば以下の通りです。 + +```rust +# #![feature(precise_capturing)] +fn f_implicit(_: impl Sized) -> impl Sized {} +// ~~~~~~~~~~ +// これが APIT です。 +// +// 上記は「概ね」以下と等価です。 +fn f_explicit<_0: Sized>(_: _0) -> impl Sized + use<_0> {} +``` + + + +ただし、これは「完全に」等価ではありません。後者のようにジェネリックパラメータを名前付きにした場合、turbofish `::<>` 構文で引数を明示することができてしまうからです。 +実際には `use<..>` 境界で匿名ジェネリックパラメータを明示したい場合、名前付きジェネリックパラメータとして定義し直すしかありません。 ## Migration From 02e549f0cb9475770a9fb71421f19f274bf57936 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Tue, 4 Feb 2025 00:52:07 +0900 Subject: [PATCH 4/8] =?UTF-8?q?WIP:=20RPIT=20lifetime=20capture=20?= =?UTF-8?q?=E3=81=AE=E3=80=8C=E7=A7=BB=E8=A1=8C=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rust-2024/rpit-lifetime-capture.md | 85 ++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/rust-2024/rpit-lifetime-capture.md b/src/rust-2024/rpit-lifetime-capture.md index 49370a8..1512d7d 100644 --- a/src/rust-2024/rpit-lifetime-capture.md +++ b/src/rust-2024/rpit-lifetime-capture.md @@ -326,37 +326,81 @@ Note that the former is not *exactly* equivalent to the latter because, by namin ただし、これは「完全に」等価ではありません。後者のようにジェネリックパラメータを名前付きにした場合、turbofish `::<>` 構文で引数を明示することができてしまうからです。 実際には `use<..>` 境界で匿名ジェネリックパラメータを明示したい場合、名前付きジェネリックパラメータとして定義し直すしかありません。 + +## 移行 + + + +### 移行時にキャプチャしすぎを回避する + +Rust 2024 で新たにキャプチャされるライフタイムは、`impl_trait_overcaptures` リントで検出可能です。 +このリントは `cargo fix --edition` で自動適用される `rust-2024-compatibility` リントの一部です。 +ほとんどの場合、リントが必要に応じて `use<..>` を自動的に挿入し、Rust 2024 への移行時にもキャプチャされるライフタイムが増えないようにします。 + + + +コードを Rust 2024 互換に移行するには、以下を実行します。 ```sh cargo fix --edition ``` + + +例えば、 ```rust fn f<'a>(x: &'a ()) -> impl Sized { *x } ``` + + +は ```rust # #![feature(precise_capturing)] fn f<'a>(x: &'a ()) -> impl Sized + use<> { *x } ``` +に変わります。 + + +`use<>` 境界がないと、返り値の不透明型が `'a` をキャプチャしてしまいます。 +しかし移行リントが `use<>` を追加したので、意味合いが変わらないようになっています。 + + + +## APIT が絡む移行 + +ジェネリックパラメータが名無しであるために `use<..>` で参照できず、リントが自動移行に失敗する場合があります。 +この場合、手動操作が必要であるとリントが報告します。 +たとえば、以下のコードを考えます。 + + + +```rust,edition2021 +fn f<'a>(x: &'a (), y: impl Sized) -> impl Sized { (*x, y) } +// ^^ ~~~~~~~~~~ +// ここで APIT が使われています。 +// +//~^ WARN `impl Sized` will capture more lifetimes than possibly intended in edition 2024 +//~| NOTE specifically, this lifetime is in scope but not mentioned in the type's bounds +// (訳) +//~^ 警告: 2024 エディションにすると、`impl Sized` が想定より多くのライフタイムをキャプチャする可能性があります。 +//~| 情報: 特に、このライフタイムはスコープ内にありますが、型境界に明示れていません +# +# fn test<'a>(x: &'a (), y: ()) -> impl Sized + 'static { +# f(x, y) +# } +``` + +これの変換が失敗するのは、ジェネリックパラメータを全部 `use<..>` 境界で列挙したいにもかかわらず、無名型を用いた APIT が含まれるからです。 +コードを Rust 2024 に変換しつつ、ライフタイムはキャプチャしないようにするには、例えば以下のように、型変数を名前付きに変更する必要があります。 + +```rust +# #![feature(precise_capturing)] +# #![deny(impl_trait_overcaptures)] +fn f<'a, T: Sized>(x: &'a (), y: T) -> impl Sized + use { (*x, y) } +// ~~~~~~~~ +// 型引数に名前がつきました。 +# +# fn test<'a>(x: &'a (), y: ()) -> impl Sized + use<> { +# f(x, y) +# } +``` + + + +なお、これは API の若干の変更にあたります。turbofish `::<>` 構文で型引数が明示的に指定できるようになったからです。 +これを避けたい場合は、引き続き `use<..>` を明示しないことでライフタイムがキャプチャされるようになっても良いか判断する必要があります。 +特に、隠蔽された型が将来的にライフタイムをキャプチャしうる余地を残したい場合は、そうした方がよいでしょう。 ### Migrating away from the `Captures` trick From b1bb17f25901226acf75cfe9fc13c47e54b3f0e7 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Tue, 4 Feb 2025 16:04:13 +0900 Subject: [PATCH 5/8] =?UTF-8?q?RPIT=20lifetime=20capture=20=E4=B8=80?= =?UTF-8?q?=E6=97=A6=E7=BF=BB=E8=A8=B3=E5=AE=8C=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rust-2024/rpit-lifetime-capture.md | 82 +++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/rust-2024/rpit-lifetime-capture.md b/src/rust-2024/rpit-lifetime-capture.md index 1512d7d..8b3f14e 100644 --- a/src/rust-2024/rpit-lifetime-capture.md +++ b/src/rust-2024/rpit-lifetime-capture.md @@ -26,7 +26,7 @@ This chapter describes changes related to the **Lifetime Capture Rules 2024** in --> - Rust 2024 では、`use<..>` を書かない限り、スコープ内の **すべての** ジェネリックパラメータ(ライフタイムパラメータを含む)が暗黙にキャプチャされます。 -- `Captures` パターン(`Captures<..>` 境界)や「長生き」パターン(`'_` 境界)は、(どのエディションでも)`use<..>` 境界に書き換えたり、(Rust 2024 では)削除したりできます。 +- `Captures` パターン(`Captures<..>` 境界)や outlive パターン(`'_` 境界)は、(どのエディションでも)`use<..>` 境界に書き換えたり、(Rust 2024 では)削除したりできます。 +### `Captures` パターンをやめる + + + +Rust 1.82 で `use<..>` 境界を用いた精密なキャプチャが使えるようになるまで、RPIT 不透明型がライフタイムを正しくキャプチャするためによく使われていたのが `Captures` パターンです。 +例えば以下の通りです。 + + +```rust +#[doc(hidden)] +pub trait Captures {} +impl Captures for U {} + +fn f<'a, T>(x: &'a (), y: T) -> impl Sized + Captures<(&'a (), T)> { +// ~~~~~~~~~~~~~~~~~~~~~ +// これがいわゆる Captures パターンです。 + (x, y) +} +# +# fn test<'t, 'x>(t: &'t (), x: &'x ()) { +# f(t, x); +# } +``` + + +`use<..>` 境界構文が導入された今、`Captures` パターンを使う必要はなくなり、どのエディションでも以下のように書き換えることができるようになりました。 ```rust # #![feature(precise_capturing)] @@ -504,7 +535,11 @@ fn f<'a, T>(x: &'a (), y: T) -> impl Sized + use<'a, T> { # } ``` + + +Rust 2024 では `use<..>` はそもそも省略可能な場合が多いです。上記は以下のように書き換えられます。 ```rust,edition2024 # #![feature(lifetime_capture_rules_2024)] @@ -517,12 +552,26 @@ fn f<'a, T>(x: &'a (), y: T) -> impl Sized { # } ``` + +これは自動移行されず、Rust 2024 でも `Captures` パターンは使用可能ですが、古い書き方からは脱却したほうがよいでしょう。 + + + +### outlive パターンをやめる + + +Rust 1.82 で `use<..>` 境界を用いた精密なキャプチャが使えるようになるまで、不透明型に隠蔽された型がライフタイムを要求する場合に outlive パターンが広く使われていました。 +例えば以下の通りです。 + +```rust +fn f<'a, T: 'a>(x: &'a (), y: T) -> impl Sized + 'a { + // ~~~~ ~~~~ + // ^ ここで outlive パターンを使っています。 + // | + // このパターンのためだけに、追加の境界が必要になっています。 + (x, y) +// ~~~~~~ +// 型 `(&'a (), T)` が隠蔽されています。 +} +``` + + + +このパターンは `Captures` パターンほど古の産物ではないですが、`Captures` パターンほど正しくもありませんでした。 +上の例のように、`T` を構成するライフタイムはライフタイム `'a` と無関係であるのに、パターンを適用するために `T: 'a` を指定せざるを得ないからです。 +関数を使う側としては、思いがけない不当な制約となってしまいます。 + + +精密なキャプチャを使えば、上記のコードはどのエディションでも以下のように書き換えられます。 ```rust # #![feature(precise_capturing)] @@ -550,7 +622,11 @@ fn f(x: &(), y: T) -> impl Sized + use<'_, T> { # } ``` + + +Rust 2024 では、 `use<..>` 境界は多くの場合で明示が不要なので、次のように書くこともできます。 ```rust,edition2024 # #![feature(precise_capturing)] @@ -564,4 +640,8 @@ fn f(x: &(), y: T) -> impl Sized { # } ``` + + +これは自動移行されず、Rust 2024 でも outlive パターンは使用可能ですが、古い書き方からは脱却したほうがよいでしょう。 From c1d3532a012a7d8030a9cb7fc12d638e1196cc41 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Tue, 4 Feb 2025 16:05:59 +0900 Subject: [PATCH 6/8] =?UTF-8?q?=E7=9B=AE=E6=AC=A1=E3=82=92=E7=B7=A8?= =?UTF-8?q?=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ab10822..cc3c472 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -114,7 +114,7 @@ - [Rust 2024](rust-2024/index.md) - [Language](rust-2024/language.md) - - [RPIT lifetime capture rules](rust-2024/rpit-lifetime-capture.md) + - [RPIT におけるライフタイムキャプチャ規則](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) From 409fd0fdb7315a857e02fe9957f2bbd7b9e2cad3 Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Tue, 4 Feb 2025 18:54:23 +0900 Subject: [PATCH 7/8] =?UTF-8?q?=E6=96=87=E4=BD=93=E3=82=92=E6=95=B4?= =?UTF-8?q?=E3=81=88=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rust-2024/rpit-lifetime-capture.md | 35 +++++++++++++------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/rust-2024/rpit-lifetime-capture.md b/src/rust-2024/rpit-lifetime-capture.md index 8b3f14e..125c816 100644 --- a/src/rust-2024/rpit-lifetime-capture.md +++ b/src/rust-2024/rpit-lifetime-capture.md @@ -44,8 +44,8 @@ This chapter describes changes related to the **Lifetime Capture Rules 2024** in *Capturing* a generic parameter in an RPIT (return-position impl Trait) opaque type allows for that parameter to be used in the corresponding hidden type. In Rust 1.82, we added `use<..>` bounds that allow specifying explicitly which generic parameters to capture. Those will be helpful for migrating your code to Rust 2024, and will be helpful in this chapter for explaining how the edition-specific implicit capturing rules work. These `use<..>` bounds look like this: --> -戻り値としての `impl Trait` (return-position impl Trait, 以下 RPIT) 不透明型が「ジェネリックパラメータをキャプチャする」とは、対応する隠された型がそのパラメータを使用できる、ということです。 -Rust 1.82 で導入された `use<..>` 境界を用いると、どのジェネリックパラメータがキャプチャされているのかを明示することができるようになりました。 +戻り値としての `impl Trait` (return-position impl Trait, 以下 RPIT) 不透明型が「ジェネリックパラメータをキャプチャする」とは、不透明型に隠蔽された型がそのパラメータを使用できる、ということです。 +Rust 1.82 では `use<..>` 境界が導入され、どのジェネリックパラメータがキャプチャされているのかを明示することができるようになりました。 この構文は Rust 2024 への移行にも活用できますし、本節では各エディションにおける暗黙のキャプチャ規則を説明するためにも用います。 `use<..>` 境界は次のように使用します。 @@ -70,7 +70,7 @@ fn capture<'a, T>(x: &'a (), y: T) -> impl Sized + use<'a, T> { # #![feature(precise_capturing)] fn capture<'a, T>(x: &'a (), y: T) -> impl Sized + use<'a, T> { // ~~~~~~~~~~~~~~~~~~~~~~~ - // これが RPIT 不透明型です。 + // これが RPIT 不透明型です。 // // `'a` と `T` をキャプチャしています。 (x, y) @@ -85,7 +85,7 @@ fn capture<'a, T>(x: &'a (), y: T) -> impl Sized + use<'a, T> { The generic parameters that are captured affect how the opaque type can be used. E.g., this is an error because the lifetime is captured despite the fact that the hidden type does not use the lifetime: --> -キャプチャされるジェネリックパラメータによって、不透明型が使える範囲も決まります。 +どのジェネリックパラメータがキャプチャされるか次第で、不透明型が使える場面も決まります。 例えば、以下のコードではライフタイム `'a` が使用されていないにもかかわらず、`'a` がキャプチャされているためエラーになります。 ```rust,compile_fail @@ -124,7 +124,7 @@ fn test<'a>(x: &'a ()) -> impl Sized + 'static { If the `use<..>` bound is not present, then the compiler uses edition-specific rules to decide which in-scope generic parameters to capture implicitly. --> -`use<..>` が明示されていない場合、スコープ内のジェネリックパラメータのうちどれをキャプチャするかは、エディションによって挙動が異なります。 +`use<..>` が明示されていない場合、スコープ内のジェネリックパラメータのうちどれがキャプチャされるかは、エディションによって挙動が異なります。 -ジェネリックなライフタイムパラメータに関しては、Rust 2021 以前のエディションでは、`use<..>` が明示されていない場合、トップレベルの関数か固有 impl 内の関連関数・メソッドにおける RPIT 不透明型の境界に現れるものだけがキャプチャされます。一方、Rust 2024 以降では、スコープ内のジェネリックなライフタイムパラメータは全てキャプチャされます。例えば以下の通りです。 +ジェネリックなライフタイムパラメータに関しては、Rust 2021 以前のエディションでは、`use<..>` が明示されていない場合、トップレベルの関数か固有 impl [^1] 内の関連関数・メソッドにおける RPIT 不透明型の境界に現れるものだけがキャプチャされます。一方、Rust 2024 以降では、スコープ内のジェネリックなライフタイムパラメータは全てキャプチャされます。例えば以下の通りです。 これは、他のシグネチャにおける RPIT 不透明型の挙動に合わせたものです。 -実際、トレイト impl における関連関数・メソッド、トレイト定義内の RPIT(Return-Position Impl Trait In Trait; RPITIT)、`asnyc fn` によって生成される不透明な `Future` 型は、 -スコープ内のジェネリックなライフタイムパラメータをエディションにかかわらず全て暗黙にキャプチャするようになっていました。 +実際、トレイト impl [^2] 内の関連関数・メソッド、トレイト定義内の RPIT(Return-Position Impl Trait In Trait; RPITIT)、`asnyc fn` によって生成される不透明な `Future` 型は、エディションにかかわらず、スコープ内のジェネリックなライフタイムパラメータを全て暗黙にキャプチャするようになっています。 + +[^1]: 訳注: 固有 impl とは、`struct MyStruct;` に対する `impl MyStruct { }` ブロックのように、型に対して直接定義される `impl` ブロックのことです。 +[^2]: 訳注: トレイト impl とは、`trait MyTrait` に対する `impl MyTrait for MyStruct { }` ブロックのように、トレイトの実装を与えるための `impl` ブロックのことです。 -引数としての impl Trait (argument position impl Trait, 以下 APIT) で作られる匿名(無名)ジェネリックパラメータは、スコープ内のパラメータと見なされます。 +引数としての impl Trait (argument position impl Trait, 以下 APIT) で作られる匿名(無名)ジェネリックパラメータも、スコープ内のパラメータと見なされます。 例えば以下の通りです。 -`use<>` 境界がないと、返り値の不透明型が `'a` をキャプチャしてしまいます。 -しかし移行リントが `use<>` を追加したので、意味合いが変わらないようになっています。 +`use<>` 境界がないと、返り値の不透明型が `'a` をキャプチャしてしまいますが、移行リントが `use<>` を追加したことにより、意味合いが変わらないようになっています。 -これの変換が失敗するのは、ジェネリックパラメータを全部 `use<..>` 境界で列挙したいにもかかわらず、無名型を用いた APIT が含まれるからです。 -コードを Rust 2024 に変換しつつ、ライフタイムはキャプチャしないようにするには、例えば以下のように、型変数を名前付きに変更する必要があります。 +ここで、変換が失敗するのは、ジェネリックパラメータを全て `use<..>` 境界で列挙したいにもかかわらず、無名型を用いた APIT が使われているからです。 +コードを Rust 2024 に変換しつつ、ライフタイムがキャプチャされないようにするには、例えば以下のように、型引数を名前付きに変更する必要があります。 -これは自動移行されず、Rust 2024 でも `Captures` パターンは使用可能ですが、古い書き方からは脱却したほうがよいでしょう。 +このような移行は自動ではなされず、また Rust 2024 でも `Captures` パターンは使用可能ですが、古い書き方からは脱却したほうがよいでしょう。 このパターンは `Captures` パターンほど古の産物ではないですが、`Captures` パターンほど正しくもありませんでした。 -上の例のように、`T` を構成するライフタイムはライフタイム `'a` と無関係であるのに、パターンを適用するために `T: 'a` を指定せざるを得ないからです。 +上の例のように、`T` を構成するライフタイムは `'a` と無関係であるのに、パターンを適用するために `T: 'a` を指定せざるを得ないからです。 関数を使う側としては、思いがけない不当な制約となってしまいます。 -これは自動移行されず、Rust 2024 でも outlive パターンは使用可能ですが、古い書き方からは脱却したほうがよいでしょう。 +このような移行は自動ではなされず、また Rust 2024 でも outlive パターンは使用可能ですが、古い書き方からは脱却したほうがよいでしょう。 From bf638c71f2ab641d2e5c20210e4cf5d2c4417d5d Mon Sep 17 00:00:00 2001 From: TonalidadeHidrica <47710717+TonalidadeHidrica@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:14:10 +0900 Subject: [PATCH 8/8] =?UTF-8?q?=E5=8E=9F=E6=96=87=E3=82=92=E3=82=88?= =?UTF-8?q?=E3=82=8A=E5=B0=8A=E9=87=8D=E3=81=97=E3=81=9F=E7=BF=BB=E8=A8=B3?= =?UTF-8?q?=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rust-2024/rpit-lifetime-capture.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rust-2024/rpit-lifetime-capture.md b/src/rust-2024/rpit-lifetime-capture.md index 125c816..2768638 100644 --- a/src/rust-2024/rpit-lifetime-capture.md +++ b/src/rust-2024/rpit-lifetime-capture.md @@ -8,8 +8,7 @@ This chapter describes changes related to the **Lifetime Capture Rules 2024** introduced in [RFC 3498], including how to use opaque type *precise capturing* (introduced in [RFC 3617]) to migrate your code. --> -本節では、[RFC 3498] で導入された**ライフタイムキャプチャ規則 2024** に関する変更事項を説明します。 -また、([RFC 3617] で導入された)不透明型への**精密なキャプチャ**を使ったコードへの移行方法も説明します。 +本節では、[RFC 3498] で導入された**ライフタイムキャプチャ規則 2024** に関する変更事項と、([RFC 3617] で導入された)不透明型への**精密なキャプチャ**を使ったコードへの移行方法を説明します。 [RFC 3498]: https://github.com/rust-lang/rfcs/pull/3498 [RFC 3617]: https://github.com/rust-lang/rfcs/pull/3617 @@ -124,13 +123,14 @@ fn test<'a>(x: &'a ()) -> impl Sized + 'static { If the `use<..>` bound is not present, then the compiler uses edition-specific rules to decide which in-scope generic parameters to capture implicitly. --> -`use<..>` が明示されていない場合、スコープ内のジェネリックパラメータのうちどれがキャプチャされるかは、エディションによって挙動が異なります。 +`use<..>` が明示されていない場合、スコープ内のジェネリックパラメータのうちどれが暗黙にキャプチャされるかは、エディションによって挙動が異なります。 スコープ内のジェネリックな型パラメータと const パラメータは、`use<..>` が明示されていないときはエディションにかかわらず全て暗黙にキャプチャされます。 +例えば以下の通りです。 -ジェネリックなライフタイムパラメータに関しては、Rust 2021 以前のエディションでは、`use<..>` が明示されていない場合、トップレベルの関数か固有 impl [^1] 内の関連関数・メソッドにおける RPIT 不透明型の境界に現れるものだけがキャプチャされます。一方、Rust 2024 以降では、スコープ内のジェネリックなライフタイムパラメータは全てキャプチャされます。例えば以下の通りです。 +ジェネリックなライフタイムパラメータに関しては、Rust 2021 以前のエディションでは、`use<..>` が明示されていない場合、トップレベルの関数か固有 impl [^1] 内の関連関数・メソッドにおける RPIT 不透明型の境界に明示的に記述されているものだけがキャプチャされます。一方、Rust 2024 以降では、スコープ内のジェネリックなライフタイムパラメータは全てキャプチャされます。例えば以下の通りです。 これは、他のシグネチャにおける RPIT 不透明型の挙動に合わせたものです。 -実際、トレイト impl [^2] 内の関連関数・メソッド、トレイト定義内の RPIT(Return-Position Impl Trait In Trait; RPITIT)、`asnyc fn` によって生成される不透明な `Future` 型は、エディションにかかわらず、スコープ内のジェネリックなライフタイムパラメータを全て暗黙にキャプチャするようになっています。 +実際、トレイト impl [^2] 内の関連関数・メソッド、トレイト定義内の RPIT(Return-Position Impl Trait In Trait; RPITIT)、`asnyc fn` によって生成される不透明な `Future` 型は、`use<..>` が明示されていない場合、エディションにかかわらず、スコープ内のジェネリックなライフタイムパラメータを全て暗黙にキャプチャするようになっています。 [^1]: 訳注: 固有 impl とは、`struct MyStruct;` に対する `impl MyStruct { }` ブロックのように、型に対して直接定義される `impl` ブロックのことです。 [^2]: 訳注: トレイト impl とは、`trait MyTrait` に対する `impl MyTrait for MyStruct { }` ブロックのように、トレイトの実装を与えるための `impl` ブロックのことです。 @@ -385,7 +385,7 @@ fn f<'a>(x: &'a ()) -> impl Sized + use<> { *x } Without this `use<>` bound, in Rust 2024, the opaque type would capture the `'a` lifetime parameter. By adding this bound, the migration lint preserves the existing semantics. --> -`use<>` 境界がないと、返り値の不透明型が `'a` をキャプチャしてしまいますが、移行リントが `use<>` を追加したことにより、意味合いが変わらないようになっています。 +`use<>` 境界がないと、Rust 2024 で返り値の不透明型が `'a` を新たにキャプチャしてしまいますが、移行リントが `use<>` を追加したことにより、意味合いが変わらないようになっています。 ジェネリックパラメータが名無しであるために `use<..>` で参照できず、リントが自動移行に失敗する場合があります。 -この場合、手動操作が必要であるとリントが報告します。 +この場合、手動の変更が必要であるとリントが報告します。 たとえば、以下のコードを考えます。 -ここで、変換が失敗するのは、ジェネリックパラメータを全て `use<..>` 境界で列挙したいにもかかわらず、無名型を用いた APIT が使われているからです。 -コードを Rust 2024 に変換しつつ、ライフタイムがキャプチャされないようにするには、例えば以下のように、型引数を名前付きに変更する必要があります。 +ここで、変換が失敗するのは、ジェネリックパラメータを全て `use<..>` 境界で「名指し」したいにもかかわらず、無名型を用いた APIT が使われているからです。 +Rust 2024 になってもライフタイムがキャプチャされないようにするには、例えば以下のように、型引数を名前付きに変更する必要があります。 -`use<..>` 境界構文が導入された今、`Captures` パターンを使う必要はなくなり、どのエディションでも以下のように書き換えることができるようになりました。 +`use<..>` 境界構文が導入された今、`Captures` パターンは不要となり、どのエディションでも以下のように書き換えることができるようになりました。 ```rust # #![feature(precise_capturing)]