構造体の基本概念
スライスやマップは、要素の型が単一である必要があります。
func main() {
s := []int{0, 1, 2, 3, 4, 5}
m := map[string]int{"name": 25, "age": 30}
fmt.Println(s, m)
}
複数の異なる型をまとめて扱いたい場合は、構造体(struct)を使用します。
type Profile struct {
name string
age int
}
func main() {
p := Profile{name: "taro", age: 28}
fmt.Printf("%v\n", p)
}
構造体は、複数のフィールドを1つのコンポジット型として定義する手法です。各フィールドは独立した型を持ちます。
自定义类型とタイプエイリアス
Goでは、既存の型をもとに新しい型を定義できます。
//独自の型としてAgeを定義
type Age uint8
Go 1.9以降では、タイプエイリアスが導入され、既存の型に別名を付けることが可能になりました。
type SimpleAge uint8 // カスタム型
type SkilledAge = uint8 // タイプエイリアス
func main() {
var sa SimpleAge
var sk SkilledAge
fmt.Printf("%T\n", sa) // main.SimpleAge
fmt.Printf("%T\n", sk) // uint8
}
自定义类型は新しい型として扱われますが、エイリアスは元の型と完全に同一視されます。インスタンス代入時の互換性にも差が出ます:
func main() {
var a Age = 20
var b SkilledAge = 25
// b = a // ❌ エラー:AgeとSkiledAgeは互換性なし
b = uint8(a) // ✅ 変換すれば代入可
}
構造体の宣言と初期化
構造体型の定義
type User struct {
ID int
Name string
Email string
Active bool
}
- 型名はパッケージ内で一意である必要があります。
- フィールド名も構造体内で一意でなければなりません。
変数の宣言方法
零値初期化
func main() {
var u User
fmt.Printf("%#v\n", u) // main.User{ID:0, Name:"", Email:"", Active:false}
u.Name = "kenji"
u.Active = true
}
構造体ポインタの生成
u1 := &User{}
u2 := new(User)
インスタンスサイズが大きい場合や、引数渡しでコピーを避けたいときにポインタ形式が有効です。
リテラル初期化(推奨)
u1 := User{ID: 101, Name: "sayaka"}
u2 := User{202, "yuki", "yuki@example.com", true}
u3 := &User{ID: 303, Name: "haru", Active: false}
- 键值対初期化:インデックス順に依存せず、可読性が高い。
- 位置配列初期化:簡潔だがメンテナンス性に注意。
匿名構造体の使用
一時的にのみ必要な小さなデータ構造には匿名構造体が便利です。
func main() {
var session struct {
token string
expires time.Time
}
session.token = "abc123xyz"
session.expires = time.Now().Add(1 * time.Hour)
fmt.Printf("型: %T, 値: %v\n", session, session)
}
匿名構造体はその場で定義・使用でき、他の変数や関数の戻り値でも活用可能です。
構造体ポインタの簡易アクセス
Goは、構造体ポインタによるフィールドアクセス時に、間接参照演算子(*ptr)を省略できるようになっています。
type Record struct {
code string
qty int
}
func main() {
r := &Record{}
r.code = "A1"
r.qty = 5
fmt.Printf("%+v\n", r) // &{code:A1 qty:5}
}
これは内部で自動的に(*r).codeに展開され、シンタックスシュガーとして提供されています。
構造体の埋め込み(Anonymous Field)
型のみを記述した匿名フィールドを用いると、外部構造体から直接内部構造体のフィールドにアクセスできます。
type Location struct {
Pref string
City string
}
type Contact struct {
mail string
}
type Member struct {
Name string
Location
Contact
Age int
}
func main() {
m := Member{
Name: "miya",
Location: Location{Pref: "Tokyo", City: "Shinjuku"},
Contact: Contact{mail: "miya@dev.net"},
Age: 34,
}
fmt.Println(m.Name, m.Pref, m.City) // Locationのフィールドを直接参照
fmt.Println(m.mail) // Contactのmailフィールドを直接取得
}
※ 同名フィールドが複数存在する場合、ambiguousエラーになるため明示的なQualified名が必要になります。