[Swift] Generics の型に制約をつけて拡張する

Swift

Generics を使用して作った RingBuffer を、制約条件を追加しながら機能拡張してみます。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Big Sur 11.3
  • Xcode 12.5
  • iOS 14.5

RingBuffer

以下の記事で、Generics を使用して、様々なタイプに使える RingBuffer を作りました。

Swift[Swift] Generics を使って RingBuffer を作る

追加したい機能 findIndex(of:)

指定した値が、Buffer に含まれているのか、含まれているならその インデックスを返すようにしてみます。

テストコード : 仕様

以下のように使える機能とします。

コード解説
  1. World は、2番目に write されたので、Index としては、2が返されるはず
  2. Aloha という値は存在しないので、Index としては、nil が返される

実装 findIndex(of:) の基本実装

実装をすすめていくと以下のようになります。

実装内容自体は、oldestIndex – latestIndex 間の要素をチェックして、一致すればその時のインデックスを返す処理を行なっています。

実装していくと問題が発生します。Generics で T というタイプを処理していますが、T の比較方法がわからないのです。 とりあえず、== と書いてますが、比較できることはだれも保証してくれないです。

実装 findIndex(of:) へ制約追加

このような時に、タイプ T に条件を追加することができるようになっています。

コード解説
  1. func 定義の後に、where 節を使用して、タイプ T に対しての制約を追加しています。
    つまり、findIndex(of:) は T が Equatable に conform している時のみ使えるメソッドになります。

where 節の効果検証

Int に対しての RingBuffer を作ってから、findIndex(of:) を使ってみます。

問題なく使えます。これは、Int が Equatable に conform しているためです。

次に、独自の struct を定義して、その struct を要素に持つ RingBuffer を定義してみます。

エラーメッセージが全てを説明していますが、MyData が Equatable に conform していないから、findIndex(of:) が使えないと言うエラーです。

なお、シンプルな struct であれば、Equatable に準拠していると宣言するだけで、Equtable に conform させることができます。
ですので、以下のように “extension MyData: Equatable {}” と追加するとエラーはなくなります。

コード解説
  1. MyData を Equtable に conform させるために、追加
    含まれるプロパティ全てが一致すると一致と判定されます。

struct 定義での placeholder にも追加できます

関数の定義時ではなく、struct 定義の時の placeholder に制約を追加することもできます。

コード解説
  1. struct の placeholder に制約を追加するにはこのように記述します
  2. struct 宣言時に T に対して制約を設定しているので、関数レベルでの制約記述は不要です

このように制約を struct に付与する時には、以下に気をつけなければいけません。

  • struct (この場合 RingBuffer) が保持できる要素は、Equatable に conform する必要が発生します

つまり、関数宣言時に制約を付与した時には、その制約を満たさない時に使えないのは、関数だけでしたが、
struct 宣言時に制約を付与すると、その制約を満たさないと struct 自体が使えなくなります。

まとめ:Generics のタイプに制約を付与する

Generics のタイプに制約を付与する
  • struct 宣言時の placeholder にも制約を付与することができる
  • struct 宣言時に制約を付与すると、制約を満たさないタイプは、struct が使用できなくなる
  • 関数宣言直後に、where 節で制約を付与することができる
  • 関数宣言で制約を付与すると、制約を満たさないタイプではその関数が使用できなくなる

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です