go-interfaces について
Go の API を設計する開発者向けに、インターフェース、型アサーション、埋め込み、レシーバーの選択を扱うスキルです。適合性確認用のスクリプトと資料も含みます。
主な機能
- 01利用側で定義するインターフェース
- 02具体型とインターフェースの返却指針
- 03安全な型アサーションと型スイッチ
- 04埋め込みとレシーバー型の指針
- 05コンパイル時の適合性チェック
活用シーン
- 01パッケージ API とコンストラクターの設計
- 02型のインターフェース適合性の確認
- 03公開構造体の埋め込みの見直し
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cxuu/ golang-skills --skill go-interfacesインストール後、Claude Code で/go-interfacesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/EMBEDDING.mdRECEIVER-TYPE.mdscripts/check-interface-compliance.gocheck-interface-compliance.sh詳細
Go のインターフェースとコンポジション
リソースルーティング
scripts/check-interface-compliance.sh- コンパイル時アサーションが必要な可能性のあるエクスポート済みインターフェースを見つけるためのヒューリスティックとして実行します。scripts/check-interface-compliance.go-check-interface-compliance.shから呼び出される実装ヘルパーです。メソッドセットの分析を変更する場合は、このファイルを修正してください。references/EMBEDDING.md- 公開 API でインターフェースや構造体を埋め込む場合に読んでください。references/RECEIVER-TYPE.md- ポインタレシーバーと値レシーバーがインターフェースの充足に影響する場合に読んでください。
インターフェースを受け取り、具体型を返す
インターフェースは、値を実装するパッケージではなく、値を利用するパッケージに置きます。コンストラクターから具体型(通常はポインター型または構造体型)を返すと、リファクタリングせずに新しいメソッドを追加できます。
// Good: consumer defines the interface it needs
package consumer
type Thinger interface { Thing() bool }
func Foo(t Thinger) string { ... }
// Good: producer returns concrete type
package producer
type Thinger struct{ ... }
func (t Thinger) Thing() bool { ... }
func NewThinger() Thinger { return Thinger{ ... } }
// Bad: producer defines and returns its own interface
package producer
type Thinger interface { Thing() bool }
type defaultThinger struct{ ... }
func NewThinger() Thinger { return defaultThinger{ ... } }
使用される前にインターフェースを定義しないでください。 現実的な使用例がなければ、そもそもインターフェースが必要かどうかを判断するのが難しくなります。
汎用性:実装を隠し、インターフェースを公開する
型が、エクスポートされたメソッドをそのインターフェース以外に持たず、インターフェースの実装だけを目的としている場合は、実装を隠すためにコンストラクターからインターフェースを返します。
func NewHash() hash.Hash32 {
return &myHash{} // unexported type
}
利点:呼び出し側に影響を与えずに実装を変更できます。アルゴリズムを差し替えるには、コンストラクターの呼び出しを変更するだけで済みます。
型アサーション:カンマ ok イディオム
確認せずにアサーションすると、失敗時にランタイムパニックが発生します。安全にテストするには、常にカンマ ok イディオムを使用してください。
str, ok := value.(string)
if ok {
fmt.Printf("string value is: %q\n", str)
}
値がインターフェースを実装しているか確認するには、次のようにします。
if _, ok := val.(json.Marshaler); ok {
fmt.Printf("value %v implements json.Marshaler\n", val)
}
型スイッチ
変数名を再利用する(t := t.(type))のが慣用的です。各 case 分岐では、変数が適切な型になります。複数の型を列挙する case(case int, int64:)では、変数はインターフェース型になります。
埋め込み
公開構造体への型の埋め込みは避けてください。内部の型のメソッドセット全体が公開 API の一部になります。代わりに、非エクスポートフィールドを使用してください。
インターフェースの充足チェック
ブランク識別子への代入を使うと、型がインターフェースを実装していることをコンパイル時に検証できます。
var _ json.Marshaler = (*RawMessage)(nil)
*RawMessage が json.Marshaler を実装していない場合、コンパイルエラーになります。
次の場合にこのパターンを使用してください。
- インターフェースを自動的に検証する静的変換がない
- 正しい動作のために型がインターフェースを満たす必要がある(例:カスタム JSON マーシャリング)
- インターフェースの変更によって、静かに動作が劣化するのではなく、コンパイルエラーが発生するべき
すべてのインターフェースにこれらのチェックを追加しないでください。他の静的変換でエラーを検出できない場合に限って追加してください。
検証:インターフェースまたは実装を定義した後、
bash scripts/check-interface-compliance.shを実行し、すべての具象型にコンパイル時のvar _ I = (*T)(nil)チェックがあることを確認してください。
レシーバー型
迷った場合は、ポインタレシーバーを使用してください。1つの型でレシーバー型を混在させないでください。ポインターが必要なメソッドが1つでもあれば、すべてのメソッドでポインターを使用してください。値レシーバーを使用するのは、小さく不変な型(Point、time.Time)または基本型に限ります。
クイックリファレンス
| 概念 | パターン | 備考 |
|---|---|---|
| 利用側がインターフェースを所有 | 使用する場所でインターフェースを定義する | 実装するパッケージには置かない |
| 安全な型アサーション | v, ok := x.(Type) | ゼロ値と false を返す |
| 型スイッチ | switch v := x.(type) | 変数は case ごとに適切な型になる |
| インターフェースの埋め込み | type RW interface { Reader; Writer } | メソッドの集合 |
| 構造体の埋め込み | type S struct { *T } | T のメソッドを昇格させる |
| インターフェースチェック | var _ I = (*T)(nil) | コンパイル時検証 |
| 汎用性 | コンストラクターからインターフェースを返す | 実装を隠す |
関連スキル
- インターフェースの命名:インターフェースの命名(
-erサフィックスの慣習)やレシーバー名の選択については、go-naming を参照してください - エラー型:
errorインターフェース、カスタムエラー型、またはerrors.Asによるマッチングを実装する際は、go-error-handling を参照してください - ジェネリクスとインターフェースの比較:ジェネリクスが必要か、既存のインターフェースで十分かを判断する際は、go-generics を参照してください
- 関数型オプション:柔軟なコンストラクターにインターフェースベースの Option パターンを使用する際は、go-functional-options を参照してください
- 防御的な境界処理:インターフェースのアサーションが、より広範な API 境界の堅牢化作業の一部である場合は、go-defensive を参照してください