diff --git a/Guide.docc/DataRaceSafety.md b/Guide.docc/DataRaceSafety.md index 0d73960..74b37e1 100644 --- a/Guide.docc/DataRaceSafety.md +++ b/Guide.docc/DataRaceSafety.md @@ -4,10 +4,10 @@ Swiftが用いる基本的な概念について学び、データ競合のない |原文|[https://www.swift.org/migration/documentation/swift-6-concurrency-migration-guide/dataracesafety](https://www.swift.org/migration/documentation/swift-6-concurrency-migration-guide/dataracesafety)| |---|---| -|更新日|2024/7/24(翻訳を最後に更新した日付)| -|ここまで反映|https://github.com/apple/swift-migration-guide/commit/24e31ffc589fefb42f08877878e689eb29b1644b| +|更新日|2024/8/11(翻訳を最後に更新した日付)| +|ここまで反映|https://github.com/apple/swift-migration-guide/commit/2e7fa65583c1cfc37724c98562bb256ecacb99d8| -従来、可変状態(mutable state)は、細心の注意を払い、ランタイム時の同期によって手動で保護する必要がありました。つまり、ロックやキューなどのツールを使用してデータ競合を防ぐのは、完全にプログラマー任せだということです。これは、正しく実行するだけでなく、ずっと正しく実行し続けることも非常に難しくあります。同期が*必要かどうか*を判断することさえも難しいかもしれません。最悪の状況は、安全でないコードを不正だと検知される保証がないことです。このコードは、多くの場合正しく動いているように見えますが、それはおそらく、データ競合の特徴である間違った予測不可能な挙動が表面化するのに、かなり特殊な条件が必要にだからでしょう。 +従来、可変状態(mutable state)は、細心の注意を払い、ランタイム時の同期によって手動で保護する必要がありました。つまり、ロックやキューなどのツールを使用してデータ競合を防ぐのは、完全にプログラマー任せだということです。これは、正しく実行するだけでなく、ずっと正しく実行し続けることも非常に難しくあります。同期が*必要かどうか*を判断することさえも難しいかもしれません。最悪の状況は、安全でないコードを不正だと検知される保証がないことです。このコードは、多くの場合正しく動いているように見えますが、それはおそらく、データ競合の特徴である間違った予測不可能な挙動が表面化するのに、かなり特殊な条件が必要だからでしょう。 より正確にいうと、データ競合は、あるスレッドがメモリにアクセスしている際に、別のスレッドが同じメモリを変更することで発生します。Swift 6言語モードでは、コンパイル時にデータ競合を防ぐことによってこれらの問題を排除します。 @@ -41,7 +41,7 @@ Swiftプログラマーは、静的と動的という2つの方法でデータ ### 非隔離(Non-isolated) -関数や変数は、明示的な隔離ドメインの一部である必要はありません。実際、隔離されていないのがデフォルトで、*非隔離(non-isolated)*と呼ばれます。すべてのデータ隔離のルールが適用されるため、非隔離のコードが別のドメインで保護されている状態を変更することはできません。 +関数や変数は、明示的な隔離ドメインの一部である必要はありません。実際、隔離されていないのがデフォルトで、*非隔離(non-isolated)*と呼ばれます。すべてのデータ隔離のルールが適用されるため、非隔離のコードは別のドメインで保護されている状態を変更できません。 ```swift func sailTheSea() { @@ -136,7 +136,7 @@ class ChickenValley { タスクは、プログラム内で並行して実行できる作業の単位です。タスクの外側でSwiftは並行コードを実行できませんが、それは常に手動で開始しなければならないということではありません。一般的に、非同期関数は、それを実行しているタスクを認識する必要はありません。実際、タスクは、多くの場合、アプリケーションフレームワーク内、あるいはプログラムのエントリーポイントといった、より高レベルで開始できます。 -複数のタスクを並行して実行することはできますが、個々のタスクは一度に1つの関数しか実行しません。タスクは、タスク内のコードを最初から最後まで順番に実行します。 +複数のタスクを並行して実行できますが、個々のタスクは一度に1つの関数しか実行しません。タスクは、タスク内のコードを最初から最後まで順番に実行します。 ```swift Task { @@ -227,7 +227,29 @@ protocol Feedable { 隔離の*推論*は、型がそのプロパティとメソッドの隔離を暗黙的に定義することを可能にします。しかし、これらはすべて*宣言*の場合です。隔離の*継承*を用いれば関数の値でも同様の効果を得られます。 -クロージャは、型によって隔離が静的に定義される代わりに、その宣言された場所で隔離をキャプチャできます。このメカニズムは複雑に聞こえるかもしれませんが、実際には非常に自然な振る舞いを可能にします。 +デフォルトでは、クロージャはクロージャを形成したコンテキストと同じコンテキストで隔離されます。例えば、 + +```swift +@MainActor +class Model { ... } + +@MainActor +class C { + var models: [Model] = [] + + func mapModels( + _ keyPath: KeyPath + ) -> some Collection { + models.lazy.map { $0[keyPath: keyPath] } + } +} +``` + +上記のコードでは、`LazySequence.map`のクロージャは`@escaping (Base.Element) -> U`型です。このクロージャは、クロージャが元々形成されたメインアクター上に留まっているはずです。これにより、このクロージャは、それを囲むコンテキストから状態をキャプチャしたり、隔離されたメソッドを呼び出したりできます。 + +元のコンテキストと並行して実行できるクロージャは、後のセクションで説明される`@Sendable`および`sending`アノテーションを通じて明示的に示します。 + +並行に評価される可能性がある`async`クロージャの場合でも、クロージャは元のコンテキストの隔離をキャプチャできます。この仕組みは、`Task`のイニシャライザで使用され、デフォルトではクロージャ内に与えられた操作が元のコンテキストに隔離される一方で、明示的な隔離も指定できます。 ```swift @MainActor @@ -237,10 +259,14 @@ func eat(food: Pineapple) { // クロージャの本体はMainActorの隔離を継承できる Chicken.prizedHen.eat(food: food) } + + Task { @MyGlobalActor in + // このタスクは`MyGlobalActor`に隔離されている + } } ``` -ここでのクロージャの型は`Task.init`によって定義されています。この宣言はどのアクターにも隔離されていませんが、新しく作られるタスクはそれを囲むスコープの `MainActor`による隔離を*継承*します。関数型は、隔離の動作を制御するためのさまざまなメカニズムを提供しますが、デフォルトでは他の型と同じように動作します。 +ここでのクロージャの型は`Task.init`によって定義されています。この宣言はどのアクターにも隔離されていませんが、新しく作られるタスクは、明示的にグローバルアクターを書かない限り、それを囲むスコープの `MainActor`による隔離を*継承*します。関数型は、隔離の動作を制御するためのさまざまなメカニズムを提供しますが、デフォルトでは他の型と同じように動作します。 > 注記: さらに詳細については、The Swift Programming Languageの[Closures][]セクションを参照してください。