はじめに
swift concurrencyの対応を行なっているとclassをSendable
に適合させる必要がある場面があり以下のように実装してみたのですが、警告が表示されコンパイラに怒られました。
class Counter: Sendable {
let value: Int = 0
}
警告の内容としては以下のとおりです。
Non-final class 'Counter' cannot conform to 'Sendable'; use '@unchecked Sendable'; this is an error in the Swift 6 language mode
今回は上記の警告での対応方法と、なぜこの警告が出たのかを調べたのでその内容を記事にしました。
警告の内容と対応方法
これはコンパイラの警告を日本語訳するとfinal出ないclassはSendable
には適合できまへんということなので、素直にclassにfinalをつけるとコンパイラを黙らせることができます。
(コンパイラは結構ちゃんと教えてくれていました)
修正後のコードは以下のとおりです。
final class Counter: Sendable {
let value: Int = 0
}
これだけでは流石に味気ない記事で終わってしまうので、冒頭でも述べたとおりなぜfinalなclass出ないとSendable
に適合できないのか以降で考えてみます。
なぜclassはfinalでないとSendable
に適合できないのか
これはSendable
の意味とfinalなclassの意味を理解すれば、理由が見えてきます。
Sendable
とは
データ競合しない型であることを保証するプロトコルです。
データ競合しないということは、型がイミュータブル(不変)であるということです。(複数のスレッドで同時に書き込みを含むアクセスがあったときにデータ競合が起きるため、書き込みが起きない型であれば書き込みができないのでデータ競合も起きない)
finalなclassとは
継承されないclassのことです。
finalがついていないclassは別クラスで継承できますが、finalがついたclassを継承しようとするとコンパイルエラーになります。
つまり継承されないことを保証したclassとなります。
final class Counter: Sendable {
let value: Int = 0
}
class inheritableCounter {
let value: Int = 0
}
class SampleCounter1: inheritableCounter {} // OK
class SampleCounter2: Counter {} // コンパイルエラー
継承すると、以下SampleCounter1
のようにプロパティの拡張が行えたり、継承元の型として振る舞うことができます。
class InheritableCounter {
let value: Int = 0
}
class SampleCounter1: InheritableCounter {
var extensionValue: Int = 0
}
let counter: InheritableCounter = SampleCounter1()
ここまでくると、なぜfinalなclassでないとSendable
に適合できないか見えてきました。
finalでないclassは継承によって ミュータブル(可変)な型になることが可能です。
そして、継承された型は継承元と同じ型として振る舞うことができる性質上、継承元がイミュータブルでも継承された型がミュータブルとなり継承元の型として振る舞うと継承元の型がイミュータブルであることつまりSendable
であることが保証されなくなってしまいます。
よってclassをSendable
に適合するにはfinalで継承されないことを保証してあげる必要があるわけでした。
(ちなみに、structは継承することができないので、finalをつけないとSendable
になれないといった制約はありません)
class InheritableCounter { // 定数しかないので、イミュータブルなクラス
let value: Int = 0
}
class SampleCounter1: InheritableCounter { // 変数を持ったミュータブルなクラス
var extensionValue: Int = 0
}
let counter: InheritableCounter = SampleCounter1() // ミュータブルなSampleCounter1がInheritableCounterとして振る舞っている
おわり
元々はちょっとした警告の対応でしたが、なぜこれがダメなのかちゃんと考えると理由もちゃんとあって謎解きみたいで楽しかったです。