Go/struct

Goにおけるstructキーワードは、複数のフィールドをまとめた複合データ型を定義するために使用されます。

1. structの基本概念

structは異なる型のデータを1つの論理的な単位にグループ化するための仕組みです。オブジェクト指向言語のクラスに似ていますが、継承の概念はありません。

2. structの宣言と定義

2.1 基本的な定義

type Person struct {
    FirstName string
    LastName  string
    Age       int
}

2.2 タグ付きフィールド

type User struct {
    ID        int    `json:"id" db:"user_id"`
    Username  string `json:"username" db:"username"`
    Email     string `json:"email,omitempty" db:"email"`
}

2.3 埋め込みフィールド

type Address struct {
    Street  string
    City    string
    Country string
}

type Employee struct {
    Name    string
    Address // 埋め込みフィールド
}

2.4 無名struct

var point struct {
    X int
    Y int
}

3. structの初期化

3.1 フィールド名指定による初期化

p := Person{
    FirstName: "John",
    LastName:  "Doe",
    Age:       30,
}

3.2 順序による初期化(非推奨)

p := Person{"John", "Doe", 30}

3.3 newによる初期化

p := new(Person) // すべてのフィールドがゼロ値で初期化される

3.4 無名structの初期化

point := struct {
    X int
    Y int
}{
    X: 10,
    Y: 20,
}

4. structの操作

4.1 フィールドへのアクセス

p := Person{FirstName: "John", LastName: "Doe", Age: 30}
fmt.Println(p.FirstName) // "John"
p.Age = 31               // フィールドの更新

4.2 埋め込みフィールドへのアクセス

e := Employee{
    Name: "John",
    Address: Address{
        Street:  "123 Main St",
        City:    "Boston",
        Country: "USA",
    },
}
fmt.Println(e.Street) // フィールド昇格: e.Address.Streetと同じ

4.3 ポインタを通したフィールドアクセス

p := &Person{FirstName: "John", LastName: "Doe", Age: 30}
fmt.Println(p.FirstName) // (*p).FirstNameと同じ

5. structメソッドの定義

5.1 値レシーバによるメソッド

func (p Person) FullName() string {
    return p.FirstName + " " + p.LastName
}

// 使用例
p := Person{FirstName: "John", LastName: "Doe"}
fmt.Println(p.FullName()) // "John Doe"

5.2 ポインタレシーバによるメソッド

func (p *Person) SetAge(age int) {
    p.Age = age
}

// 使用例
p := Person{Age: 30}
p.SetAge(31) // (&p).SetAge(31)と同じ
fmt.Println(p.Age) // 31

6. structの特殊な使い方

6.1 空struct

type Void struct{}
void := struct{}{} // メモリを消費しない空構造体

// setとしての利用
set := make(map[string]struct{})
set["apple"] = struct{}{}

6.2 匿名struct

data := []struct {
    Name string
    Age  int
}{
    {"John", 30},
    {"Jane", 25},
}

6.3 structのコピー

p1 := Person{FirstName: "John", LastName: "Doe", Age: 30}
p2 := p1 // p1のすべてのフィールドがp2にコピーされる

6.4 structを含む型定義

type PersonMap map[string]Person
type PersonList []Person
type IntPair struct{ First, Second int }

7. インターフェースとの関係

7.1 インターフェースの実装

type Stringer interface {
    String() string
}

func (p Person) String() string {
    return fmt.Sprintf("%s %s (%d)", p.FirstName, p.LastName, p.Age)
}

// Personは暗黙的にStringerインターフェースを実装
var s Stringer = Person{"John", "Doe", 30}

8. 構造体の比較

8.1 等値比較

p1 := Person{FirstName: "John", LastName: "Doe", Age: 30}
p2 := Person{FirstName: "John", LastName: "Doe", Age: 30}
fmt.Println(p1 == p2) // true(すべてのフィールドが同じ値)

8.2 比較不能なフィールドを含む構造体

type Data struct {
    Values []int // スライスは比較不能
}

d1 := Data{Values: []int{1, 2, 3}}
d2 := Data{Values: []int{1, 2, 3}}
// fmt.Println(d1 == d2) // コンパイルエラー

9. JSONとの相互変換

9.1 structからJSONへのエンコード

type Product struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Price int    `json:"price,omitempty"`
}

p := Product{ID: 1, Name: "Apple"}
jsonData, err := json.Marshal(p)
// {"id":1,"name":"Apple"}

9.2 JSONからstructへのデコード

jsonStr := `{"id":2,"name":"Banana","price":200}`
var p Product
err := json.Unmarshal([]byte(jsonStr), &p)

10. リフレクション操作

10.1 structのリフレクション

p := Person{FirstName: "John", LastName: "Doe", Age: 30}
t := reflect.TypeOf(p)
v := reflect.ValueOf(p)

for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    value := v.Field(i)
    fmt.Printf("%s: %v\n", field.Name, value.Interface())
}

10.2 タグへのアクセス

type Tagged struct {
    Field1 string `json:"f1" validate:"required"`
    Field2 int    `json:"f2,omitempty"`
}

t := reflect.TypeOf(Tagged{})
field := t.Field(0)
fmt.Println(field.Tag.Get("json"))     // "f1"
fmt.Println(field.Tag.Get("validate")) // "required"

11. 並行処理での構造体使用

11.1 ミューテックスを埋め込んだ構造体

type SafeCounter struct {
    sync.Mutex
    count int
}

func (c *SafeCounter) Increment() {
    c.Lock()
    defer c.Unlock()
    c.count++
}

以上がGoのstructキーワードの主な用途と使い方です。構造体はGoプログラミングの基本的な構成要素であり、データの論理的なグループ化、メソッドの関連付け、interfaceの実装など多くの場面で活用されます。

カテゴリ:Go
カテゴリ:Go