[Go言語] 初心者必見シリーズ: 構造体(Structs)


構造体

構造体は、フィールド(field)の集まりです。

定義方法

構造体は下記のように typestruct を使用して定義します。

  • 構文
  type 構造体の名前 struct {
      フィールド名 型名
      フィールド名 型名
      フィールド名 型名
      ...
  }

同じ型のフィールド名は、カンマ区切りで書くことができます。

  type 構造体の名前 struct {
      フィールド名, フィールド名, フィールド名 型名
      フィールド名 型名
  }

人のデータをまとめた構造体です。

    type People struct {
            Name string
            Sex string
            Age int
            Height int
            Weight int
    }

初期化

方法1:Key:Valueで初期化します。

people := People{Name: "太郎"}

設定しない要素は ゼロ値 で初期化される。

方法2:要素の定義順に初期化します。

people := People{"太郎", "男", ...}

この方法では、全部のfieldを設定する必要があります。

方法3:定義後にドット(.)を用いてfieldを初期化します。

people := People{}
people.Sex = "男"
people3.Age = 8

構造体へのポインター(Pointer to structs)

方法1:& (アドレス演算子)で構造体へのPointerを取得します。

people := People{Name: "太郎"}
p := &people
(*p).Name = "花子"

(*p)p に省略できます。

p.Name = "花子"

方法2: new(構造体名) で構造体へのポインターを生成します。

people := new(People) // Peopleのポインタ型
(*people).Name = "花子"
people.Name = "花子"  // (*people) は peopleに省略できます。

new(T) はT型の無名変数(unnamed variable)を作成し、それを T のゼロ値へ初期化し、 *T 型の値であるそのアドレスを返します。

new(People)&People{} は等価です。

タグ(Tag)

Go構造体タグは、Go構造体宣言のフィールドの後に表示される注釈です。

実行時にreflectパッケージでその情報を取得し利用したりします。

良くある構造体をJSONに変換する例として、

下記のようにフィールドの後ろに json タグをつけると、

  type People struct {
          Name string       `json:"name"`
          Sex string        `json:"sex"`
          Age int               `json:"age"`
          Height int        `json:"height"`
          Weight int        `json:"weight"`
  }

Go言語の json 標準パッケージで構造体をJSONに変換すると、フィールド名がTagで定義された名前に置換されます。

people := People{Name: "太郎", Sex: "男"}
jsonBytes, _ := json.Marshal(people)
fmt.Println(string(jsonBytes)) 
// JSON: {"name": "太郎", "sex": "男"}

その仕組み

jsonパッケージの内部で下記のようにタグ情報を取得してJSONデータに変換しています。

 t := reflect.TypeOf(people)
 for i := 0; i < t.NumField(); i++ {
     field := t.Field(i)
     tag := field.Tag.Get("json") //  `json`タグ情報を取得
 }

下記のように、複数のタグはスペースで区切って記述できます。

  type People struct {
          Name string       `json:"name" validate:"required"`
          Sex string        `json:"sex" validate:"required"`
          Age int               `json:"age" validate:"required"`
          Height int        `json:"height" validate:"required"`
          Weight int        `json:"weight" validate:"required"`
  }

埋め込みフィールド(Embedded fields)

型だけで宣言されていて、明示的なフィールド名がないフィールドを埋め込みフィールドと呼びます。

Go言語は埋め込みで他言語のクラスの継承ぽいことができます。

埋め込み子構造体(Address)のフィールドが昇格し、構造体の外(Person)からダイレクトにアクセスすることが可能です。

person.Adress.city  person.city
person.Adress.state  person.state
// PersonがAdressを継承しているように、Adressのフィールドをダイレクトにアクセスすることが可能となります。

Person に同名のフィールドやメソッドがあるときは、Address にあるそのフィールドは昇格されないです。

構造体の比較

reflect.DeepEqual はポインターか否かにかかわらず、

フィールドの値が同じであれば true を返します。

import (
    "reflect"
)

...
people1 := People{Name: "太郎"}
people2 := People{Name: "太郎"}

println(reflect.DeepEqual(people1, people2))     // true
println(reflect.DeepEqual(&people1, &people2))   // true
println(people1 == people2)    // true
println(&people1 == &people2)  // false

動画(YouTube)

元記事