🍊

Swift: Task.detachedのoperationには@_implicitSelfCaptureが付いていない

に公開

環境

  • Swift 5.10

SE-0304 Structured concurrencyにおける記述

https://github.com/swiftlang/swift-evolution/blob/main/proposals/0304-structured-concurrency.md#implicit-self

The closure passed to Task is executed immediately, and the only reference to self is what occurs in the body. Therefore, the explicit self. isn't communicating useful information and should not be required.

Note: The same applies to the closure passed to Task.detached and TaskGroup.addTask.

2024/07/13現在、SE-0304 Structured concurrencyには Task.detached にも Task.init と同様に、クロージャに @_implicitSelfCapture が付くよ的なことが書いてある。

しかし、実際には付いていない。実装と異なっている。

コード上の様子

Task.init(priority:operation:)

https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/stdlib/public/Concurrency/Task.swift#L602-L605

operation@_implicitSelfCapture が付いている。

Task.detached(priority:operation:)

https://github.com/swiftlang/swift/blob/48015abbd89116163115e1d728e8478829c91271/stdlib/public/Concurrency/Task.swift#L725-L728

operation@_implicitSelfCapture が付いておらず、Swift Evolutionの記述と異なっている。

@_implicitSelfCapture とは

https://github.com/swiftlang/swift/blob/main/docs/ReferenceGuides/UnderscoredAttributes.md#_implicitselfcapture

Selfが参照型の場合でも、明示的にキャプチャせずにクロージャ内のselfにアクセスできるようする。

swiftlang/swiftにおいて、Concurrency文脈以外では特に使われていなさそうだった。

https://github.com/search?q=repo%3Aswiftlang%2Fswift %40_implicitSelfCapture&type=code

考察

Task.init は呼び出し側のActor上で実行され、クロージャの実行は速やかに行われる。そのため、クロージャ内のselfへの参照が発生しても、本体による参照となるため循環参照は起こらないだろう。

一方で、Task.detached は呼び出し側のActorとは別でクロージャの実行が行われる。スレッドが変わるため、selfの参照をクロージャ内で持つと循環参照が発生する。よって、開発者側で明にselfをキャプチャさせた方が意識的に循環参照の発生リスクを減らせるため、今のまま @_implicitSelfCapture は付けない方が良いと思う。

ちなみに、去年に同じ内容でフォーラムに上がっていて、似たようなコメントがある。

https://forums.swift.org/t/task-detached-doesn-t-allow-implicit-self/63862/3

参考

Discussion