Swift Digest
SE-0186 | Swift Evolution

Remove ownership keyword support in protocols

Proposal
SE-0186
Authors
Greg Spiers
Review Manager
Ted Kremenek
Status
Implemented (Swift 4.1)

01 何が問題だったのか

プロトコルのプロパティ要件には weakunowned といった所有権キーワードを付けて宣言できました。しかし、これらのキーワードはプロトコル側では何の効力も持たず、適合する型側の挙動を強制することもありません。

class A {}

protocol P {
    weak var weakVar: A? { get set }
}

class B: P {
    var weakVar: A? // weak を付けなくても警告もエラーも出ない
}

上記のように、プロトコル Pweak var を要求しているように見えても、適合する B 側で weak を付けるかどうかは自由であり、コンパイラは何のチェックも行いません。読み手は「このプロパティは weak 参照として保持されるはずだ」と誤解しかねず、実際の挙動とのずれが静かに紛れ込みます。

これは、プロトコル拡張における final を削除した SE-0164 と同じ問題です。意味を持たないキーワードが書けてしまう状況は、構文が実装の保証を与えているかのような誤った印象を与え、進化の足かせにもなります。

02 どのように解決されるのか

プロトコルのプロパティ要件に weakunowned を書くことを禁止し、誤解を生む構文そのものを取り除きます。所有権の扱いは、あくまでプロトコルに適合する側の実装で決めるべきものだという立場を明確にする変更です。

class A {}

protocol P {
    weak var weakVar: A? { get set }    // Swift 5 以降ではエラー
    unowned var unownedVar: A { get set } // 同上
}

Swift 3 / 4 モードではコンパイラが警告を出し、該当キーワードを削除する fix-it を提示します。Swift 5 モードではエラーとなり、同様の fix-it で修正を促します。既存コードを見かけ上破壊しますが、もともと意味を持っていなかった記述を取り除くだけなので、実行時の挙動は変わりません。

プロトコルを書く側は、weak / unowned を要件から外し、代わりに { get set } などの通常のプロパティ要件として宣言します。

protocol P {
    var ref: A? { get set } // weak を外し、通常の要件として宣言
}

class B: P {
    weak var ref: A? // 適合側で weak を付けるかどうかを決める
}

なお、「プロトコル側で weak / unowned に意味を持たせるべきではないか」という議論はありますが、これは追加的な機能であり別の提案として扱うべきとされ、本提案のスコープには含まれません。