Go言語は、Googleによって開発された静的型付けのプログラミング言語であり、そのシンプルさと強力な型システムが特徴です。
システム開発において、データ型を正しく理解して使い分けることは、メモリ効率の向上や予期せぬバグの回避に直結します。
2026年現在のソフトウェア開発においても、Go言語の明快な型定義は、保守性の高い大規模システムを構築する上での大きな武器となっています。
本記事では、Go言語で利用可能なデータ型について、基本的な数値型から、より高度な構造体やインターフェース、ジェネリクスまでを網羅して解説します。
初心者の方から、改めて仕様を確認したい中級者の方まで、幅広く役立つ情報を整理してご紹介します。
Go言語における型の重要性と分類
Go言語の型システムは、開発者が意図しないデータの混入を防ぎ、コンパイル時に多くのエラーを検出することを可能にします。
Go言語のデータ型は、大きく分けて「基本型」「複合型」「参照型」「インターフェース型」の4つのカテゴリーに分類できます。
基本型には数値、浮動小数点数、複素数、文字列、真偽値が含まれます。
複合型は、配列や構造体のように複数のデータを一つの単位としてまとめる型を指します。
参照型は、スライス、マップ、チャネル、ポインタのように、実際のデータへの参照を保持する型です。
最後に、インターフェース型は、具体的な実装を持たず、メソッドのシグネチャのみを定義することで多態性を実現します。
基本データ型:数値と真偽値
基本型は、Go言語のプログラムにおける最も最小の構成単位となるデータ型です。
整数型 (Integer Types)
Go言語には、符号の有無やビットサイズに応じて多くの整数型が用意されています。
符号付き整数型は int、符号なし整数型は uint というキーワードで定義されます。
プラットフォームに依存する型として int と uint があり、これらは実行環境のアーキテクチャ (32bitまたは64bit) に応じてサイズが決定されます。
| 型名 | ビットサイズ | 説明 |
|---|---|---|
int8 | 8bit | -128 ~ 127 |
int16 | 16bit | -32768 ~ 32767 |
int32 / rune | 32bit | Unicodeコードポイントの保持に使用 |
int64 | 64bit | 大きな整数の保持に使用 |
uint8 / byte | 8bit | 0 ~ 255 (バイナリデータの処理に多用) |
uint32 | 32bit | 正の整数のみを保持 |
uintptr | 環境依存 | ポインタのビットパターンをそのまま保持するための型 |
整数型を扱う際には、異なる型同士の演算に注意が必要です。
Go言語では、たとえ int32 と int64 の間であっても、暗黙的な型変換は行われません。
必ず明示的な型変換を行う必要があるため、厳密なコーディングが求められます。
浮動小数点数型と複素数型
実数を扱うための浮動小数点数型には、float32 と float64 があります。
精度の観点から、現代の多くのアプリケーションでは float64 が標準的に使用されます。
また、科学技術計算などで利用される複素数型として complex64 と complex128 も提供されています。
package main
import "fmt"
func main() {
var pi float64 = 3.1415926535
var c complex128 = complex(1, 2) // 1 + 2i
fmt.Printf("Pi: %f\n", pi)
fmt.Printf("Complex: %v\n", c)
}
Pi: 3.141593
Complex: (1+2i)
真偽値型 (Boolean Type)
真偽値を表す型は bool であり、値は true または false のいずれかです。
条件分岐の if 文や for 文の継続条件などで頻繁に使用されます。
他の言語で見られるような「0がfalse、それ以外がtrue」といった暗黙の解釈は存在しません。
基本データ型:文字列とルーン
テキスト情報を扱うための型も、Go言語においては非常に重要です。
文字列型 (String Type)
文字列型 string は、不変 (immutable) なバイトのシーケンスです。
一度作成された文字列の中身を直接書き換えることはできません。
Goの文字列は内部的に UTF-8 でエンコードされており、日本語のようなマルチバイト文字もネイティブにサポートしています。
ルーン型 (Rune Type)
rune は int32 のエイリアスであり、1つの Unicode コードポイントを表します。
文字列を文字単位で処理したい場合、string を []rune に変換することで、各文字を安全に操作できます。
package main
import "fmt"
func main() {
s := "Go言語"
runes := []rune(s)
fmt.Printf("文字列の長さ: %d\n", len(s)) // バイト数
fmt.Printf("文字数: %d\n", len(runes)) // 文字数
}
文字列の長さ: 8
文字数: 4
複合データ型:配列とスライス
複数のデータを並べて扱うための型について見ていきましょう。
配列 (Array)
配列は、固定された長さを持つ、同一の型の要素の集まりです。
型宣言の一部に長さが含まれるため、[3]int と [5]int は異なる型として扱われます。
メモリ上の連続した領域を占有するため高速ですが、後からサイズを変更できないという制約があります。
スライス (Slice)
スライスは、Go言語で最も頻繁に使用されるデータ構造であり、動的にサイズを変更できる配列のような役割を果たします。
内部的には、基礎となる配列へのポインタ、長さ (len)、容量 (cap) の3つの情報を持つ構造体として実装されています。
append 関数を使用することで、要素を簡単に追加できるのが特徴です。
package main
import "fmt"
func main() {
// スライスの初期化
nums := []int{1, 2, 3}
fmt.Println("初期状態:", nums)
// 要素の追加
nums = append(nums, 4, 5)
fmt.Println("追加後:", nums)
fmt.Printf("長さ: %d, 容量: %d\n", len(nums), cap(nums))
}
初期状態: [1 2 3]
追加後: [1 2 3 4 5]
長さ: 5, 容量: 6
マップ (Map)
マップは、キーと値のペアを保持するハッシュテーブルの実装です。
他言語における辞書や連想配列に相当します。
キーには比較可能な型 (比較演算子 == が使える型) を指定する必要があります。
マップは参照型であるため、初期化には make 関数を使用するのが一般的です。
package main
import "fmt"
func main() {
// マップの作成
userScores := make(map[string]int)
userScores["Alice"] = 95
userScores["Bob"] = 80
fmt.Println("Aliceのスコア:", userScores["Alice"])
// 要素の削除
delete(userScores, "Bob")
fmt.Println("Bobの削除後:", userScores)
}
Aliceのスコア: 95
Bobの削除後: map[Alice:95]
構造体 (Struct)
構造体は、異なる型のデータを一つのグループとしてまとめるためのユーザー定義型です。
Go言語にはクラスが存在しませんが、構造体にメソッドを定義することで、オブジェクト指向的な設計が可能です。
構造体の定義と初期化
構造体は struct キーワードを使用して定義します。
各フィールドには名前と型を指定し、必要に応じてタグ (JSON変換の指定など) を付与することもできます。
package main
import "fmt"
type User struct {
ID int
Name string
Age int
}
func main() {
// フィールド名を指定して初期化
u := User{
ID: 1,
Name: "Taro",
Age: 25,
}
fmt.Printf("User: %+v\n", u)
}
User: {ID:1 Name:Taro Age:25}
匿名構造体
特定の場所で一度だけ使用したいデータ構造がある場合、名前を付けずに構造体を定義することも可能です。
これを匿名構造体と呼び、主にテストコードや使い捨てのデータ処理で活用されます。
ポインタ (Pointer)
Go言語にはポインタが存在しますが、C言語のような複雑なポインタ演算は許可されていません。
ポインタを使用することで、値のコピーを避け、効率的にデータを共有することができます。
変数名の前に & を付けるとアドレスを取得でき、ポインタ型の変数の前に * を付けると、そのアドレスが指す値にアクセスできます。
インターフェース (Interface)
インターフェースは、振る舞い (メソッドの集まり) を定義する型です。
Go言語のインターフェースは「ダックタイピング」に近い性質を持っており、明示的な implements 宣言は不要です。
ある型がインターフェースで定義されたメソッドをすべて実装していれば、その型はそのインターフェースを満たしているとみなされます。
空のインターフェースと any
Go 1.18 以降、すべての型と互換性がある any キーワードが導入されました。
これは interface{} のエイリアスであり、どんな型の値でも受け取ることができます。
ただし、乱用すると型安全性が失われるため、適切な型アサーションや型スイッチによる検証が推奨されます。
package main
import "fmt"
func printValue(v any) {
switch t := v.(type) {
case int:
fmt.Printf("整数型です: %d\n", t)
case string:
fmt.Printf("文字列型です: %s\n", t)
default:
fmt.Println("未知の型です")
}
}
func main() {
printValue(100)
printValue("Hello")
}
整数型です: 100
文字列型です: Hello
ジェネリクス (Generics)
ジェネリクスは、特定の型に依存しない汎用的な関数や構造体を定義するための仕組みです。
型パラメータを使用することで、異なる型に対して同じロジックを適用でき、コードの重複を大幅に削減できます。
[T any] のように記述することで、型 T を引数として受け取る関数を定義できます。
型のゼロ値と型変換
Go言語では、変数を宣言した際に初期値を指定しない場合、その型に応じた「ゼロ値」が自動的に割り当てられます。
- 数値型:
0 - 真偽値型:
false - 文字列型:
""(空文字列) - 参照型 (スライス、マップ、ポインタなど):
nil
また、先述の通り Go言語では厳密な型チェックが行われます。
型の変換が必要な場合は、T(v) という形式で明示的にキャストを行う必要があります。
精度の低い型から高い型への変換であっても、この明示的な記述は省略できません。
まとめ
Go言語のデータ型は、シンプルでありながら、現代的なソフトウェア開発に必要な柔軟性と安全性を兼ね備えています。
基本型から始まり、スライスやマップといった強力な参照型、そして多態性を支えるインターフェースやジェネリクスまで、それぞれの役割は非常に明確です。
これらの型を適切に使い分けることで、パフォーマンスの高い実行バイナリと、可読性の高いソースコードを同時に実現することが可能になります。
特にスライスの容量管理や、インターフェースによる抽象化は、Go言語らしいプログラミングを実践する上で欠かせない要素です。
まずは基本となる数値型や文字列型の扱いに慣れ、徐々に構造体やインターフェースを駆使した高度な設計に挑戦してみてください。
この記事が、皆さんのGo言語学習における体系的な理解の助けになれば幸いです。
