any vs interface{} in Go: 違いは何ですか?
Ethan Miller
Product Engineer · Leapcell

any
vs interface{}
in Go: 違いは何ですか?
最近のチームの議論で、誰かが「Goのany
とinterface{}
は同じだ」と主張しました。厳密に言えば、彼らは正しいです—Goの公式な定義では、any
はinterface{}
のエイリアスになっています:
type any = interface{}
これらは内部的には機能的に同等です。では、なぜGoチームはany
を導入したのでしょうか? セマンティクスと可読性です:
interface{}
: 「制約のない動的な型」を表します(JSONの解析、リフレクションなどを考えてください)。動的な性質を強調し、型アサーションが必要です。any
: ジェネリクスのために特別に設計されています。制約のない型パラメータを示し、型安全な汎用性を強調します—一度型が設定されると、一貫性が保たれます。
この区別は、下位互換性を維持しながら、ジェネリクスのコードを明確にします。
Goのジェネリクスが解決する問題
Go 1.18(ジェネリクスが登場したとき)以前は、開発者は異なる型で同一のロジックのコードを繰り返していました。例:数値のスライスの合計:
// Sum []int64 func SumInts(numbers []int64) int64 { var s int64 for _, v := range numbers { s += v } return s } // Sum []float64 (同じロジック、異なる型) func SumFloats(numbers []float64) float64 { var s float64 for _, v := range numbers { s += v } return s }
これはDRY(Don't Repeat Yourself)に違反し、メンテナンスコストを増加させます。
Goジェネリクス:コアコンセプト
Go 1.18では、3つの主要なアイデアとともにジェネリクスが導入されました:
- 型パラメータ: 関数/型がパラメータ化された型を使用できるようにします。
- 型制約: インターフェースを介してパラメータの有効な型を定義します。
- 型推論: 呼び出し中に型を自動的に推論し、コードを簡素化します。
ジェネリクスを使用したSum関数のリファクタリング
// ジェネリック関数:int64またはfloat64で動作します func SumNumbers[T int64 | float64](numbers []T) T { var s T for _, v := range numbers { s += v } return s }
[T int64 | float64]
は型パラメータを宣言します:T
は型変数、int64 | float64
は共用体制約です。- 呼び出すときに型を指定する必要はありません(コンパイラが推論します):
ints := []int64{1, 2, 3} floats := []float64{1.1, 2.2, 3.3} fmt.Println(SumNumbers(ints)) // 6 fmt.Println(SumNumbers(floats)) // 6.6
再利用可能な型制約
複雑な/再利用可能な制約のために、それらをインターフェースとして定義します:
// 数値の制約を定義します type Number interface { int64 | float64 } // カスタム制約でリファクタリング func SumNumbers[T Number](numbers []T) T { var s T for _, v := range numbers { s += v } return s }
これにより、可読性と保守性が向上します。
Goジェネリクスの仕組み:パフォーマンスのバランス
Goのジェネリクスは、ユニークなアプローチのおかげで効率的です:GCシェイプモノモルフィゼーション + ディクショナリ(リフレクションやv-テーブルではありません):
-
GCシェイプモノモルフィゼーション コンパイラは、型の「GCシェイプ」(サイズ、アライメント、ポインタの有無)に基づいてコードを生成します。例:
int32
、uint32
、float32
は同じシェイプ(4バイト、ポインタなし)を共有 → コードを再利用します。- すべてのポインタ型(
*int
、*string
)はシェイプを共有します → コードを再利用します。
これにより、「コードの肥大化」を回避しながら、ネイティブのパフォーマンスに匹敵します。
-
ディクショナリテクニック 同じシェイプだが異なる動作(例:
int
vsfloat32
の加算)を持つ型の場合、コンパイラは隠された「ディクショナリ」を使用して、型固有の情報を(メソッドアドレス、操作関数)渡します。
開発者への実際の影響
- ほぼネイティブのパフォーマンス: 算術演算は非ジェネリックコードの速度に匹敵します。ディクショナリベースのメソッド呼び出しのオーバーヘッドは最小限です(インターフェース呼び出しと同等)。
- 制御されたバイナリサイズ: 同じ「GCシェイプ」の型のコード再利用により、肥大化を防ぎます。
結論
any
とinterface{}
は技術的には同等ですが、それらのセマンティクスはGoの型システムの進化を示しています:動的な型付けのためのinterface{}
、ジェネリクスのためのany
。これを理解することは、より明確で、よりイディオム的なGoを書くのに役立ちます。
Leapcell:最高のサーバーレスWebホスティング
Goサービスをデプロイするための推奨プラットフォーム:Leapcell
🚀 お気に入りの言語で構築
JavaScript、Python、Go、またはRustで簡単に開発できます。
🌍 無制限のプロジェクトを無料でデプロイ
使用量に応じてお支払いください。リクエストも料金もかかりません。
⚡ 従量課金制、隠れたコストなし
アイドル料金はなく、シームレスなスケーラビリティだけです。
🔹 Twitterでフォロー:@LeapcellHQ