💊

Goジェネリクスの型制約で迷わないための実務ガイド

に公開

🚀 Goジェネリクスの型制約で迷わないための実務ガイド

📝 はじめに

Go 1.18 以降、ジェネリクスが導入されました。
便利になった一方で「型制約(type constraint)」の書き方で迷う人は多いと思います。

特に混乱しやすいのが次の3つです:

  1. ~int~Hoge の違い
  2. type Hoge = inttype Hoge int の違い
  3. 「Hogeを基にした型だけ」を許可したいときのマーカーインターフェースの使い方

この記事では、それぞれをコード例・解説を交えながら整理し、最後に 「実務ではどう選べばいいか?」 の判断指針をまとめます。

📖 Go ジェネリクスと型制約の基本

型パラメータ

Goのジェネリクスは「関数や型にパラメータ化された型」を渡せる仕組みです。

func PrintAll[T any](values []T) {
    for _, v := range values {
        fmt.Println(v)
    }
}
  • T が型パラメータ
  • any は「どんな型でもOK」という制約

型制約(constraint)

型パラメータには「どんな型が渡せるか」を制約できます。
例えば「整数っぽい型だけ」を許可したい場合:

type Integer interface {
    int | int64 | uint
}

func Sum[T Integer](a, b T) T {
    return a + b
}
  • Integer が制約インターフェース
  • int | int64 | uint が「型集合」

型集合(type set)

制約インターフェースは「その型が取り得る集合」を定義します。

  • any → すべての型
  • int | string → int か string
  • ~int基底型が int のすべての型
  • ~HogeHoge がエイリアスの場合のみ有効

📌 1. ~int~Hoge の違い

~int

基底型が int のすべての型」を許可します。

type MyInt int
type Another int

type IntFamily interface {
    ~int
}

👉 OK: int, MyInt, Another

~Hoge

Hoge
エイリアスtype Hoge = int)で定義した場合のみ有効です。

type Hoge = int
type Fuga Hoge

type HogeFamily interface {
    ~Hoge
}

👉 OK: int, Hoge, Fuga
👉 NG: type Hoge int の場合はエラーになる

📌 2. type Hoge = inttype Hoge int の違い

型定義(defined type)

type Hoge int
  • 新しい型を作る
  • int とは別物
  • 制約では ~int を使う

型エイリアス(alias)

type Hoge = int
  • int の別名
  • ~Hoge で制約を書ける
  • 実際の挙動は int と完全に同じ

📌 3. マーカーインターフェースで制御する

~int だと「全部の int 系」を許可してしまいます。
もし Piyo のように int
ベースだけど対象外にしたい場合はマーカーを使います。

type Hoge int
type Fuga Hoge
type Piyo int // ← 除外したい

type HogeFamily interface {
    ~int
    isHogeFamily()
}

// Hoge 系の型だけマーカーを実装
func (Hoge) isHogeFamily() {}
func (Fuga) isHogeFamily() {}

👉 こうすることで「Hoge 系だけ」をジェネリクスの対象にできます。

🙅‍♂️ よくある誤解と落とし穴

  • type Hoge inttype Hoge = int は同じではない
    → 前者は「新しい型」、後者は「別名」

  • ~Hoge は常に使えるわけではない
    → エイリアス型のときだけ

  • マーカーインターフェースは魔法ではない
    → 型側に isHogeFamily() を実装しないと効力がない

📊 実務での選び方(まとめ)

やりたいこと 選ぶべき書き方 実務での推奨度
int 系全般を許可したい ~int ✅ よく使う
特定系列だけ許可したい マーカーインターフェース 🛠 必要に応じて
int と完全に同一扱いしたい type Hoge = int + ~Hoge ⚠️ レアケース

🎯 結論(実務で迷わない指針)

  • まずは type Hoge int + ~int を基本にする
  • ⚠️ ~Hoge は特殊用途なので実務で出番はほぼない
  • 🛠 系列を制御したいときだけマーカーを使う

👉 迷ったら「type Hoge int + ~int」でOK。
特殊な場面に遭遇したときだけ「マーカー」や「エイリアス」を検討すれば十分です。


この記事を読めば、ジェネリクスの型制約で迷ったときの判断基準がすぐに思い出せるはずです。
ぜひ現場での実装に役立ててください。

Discussion