Go における構造体の利用と応用

構造体の基本概念

スライスやマップは、要素の型が単一である必要があります。

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名が必要になります。

タグ: struct anonymous-field golang type-alias composition

5月29日 03:15 投稿