【Go言語】構造体の初期化方法の種類まとめ

Go言語

Go言語の構造体の初期化方法には以下のパターンがあります。

  • ポインタ型を使わない場合
    • 『変数定義 → フィールド値のセット』と2段階で初期化する方法
    • 構造体リテラルでフィールド値をセットする方法
  • ポインタ型を使う場合
    • アドレス演算子と構造体リテラルを利用する方法
    • newを利用する方法

以下では各方法について紹介をします。

ポインタ型を使わない場合

ポインタ型を使わない場合の構造体の初期化方法について説明します。

『変数定義 → フィールド値のセット』と2段階で初期化する方法

構造体の変数を定義し、定義した変数に構造体のフィールド値をセットする方法です。
変数を定義してからフィールド値をセットするため、複数行で構造体の初期化をします。

User構造体をuser変数として扱う場合、変数定義の方法には以下の種類があります。

変数定義の方法
  • user := User{}
  • var user User = User{}
  • var user User

以下の結果からわかるように、上記の変数定義の方法はすべて同じ意味をもちます。

package main

import "fmt"

type User struct {
  name string
  age  int
}

func main() {
  user_1 := User{}
  var user_2 User = User{}
  var user_3 User

  fmt.Println(user_1) // { 0}
  fmt.Println(user_2) // { 0}
  fmt.Println(user_3) // { 0}
}

フィールド値のセットは[変数].[フィールド名] = [値]で行います。

package main

import "fmt"

type User struct {
    name string
    age  int
}

func main() {
    user := User{}
    user.name = "Suzuki"
    user.age = 20

    fmt.Println(user) // {Suzuki 20}
}

構造体リテラルでフィールド値をセットする方法

構造体リテラル(Structure Literals)を利用した場合、1行で構造体の初期化ができます。

構造体リテラルを利用したフィールド値のセット方法には以下の2種類があります。

フィールド値のセット方法
  • フィールド名を指定する方法
  • フィールド名を指定しない方法

フィールド名を指定する場合、任意のフィールドを任意の順番でセットできます。

package main

import "fmt"

type User struct {
  name string
  age  int
}

func main() {

  user_1 := User{name: "Suzuki", age: 20}
  fmt.Println(user_1) // {Suzuki 20}

  user_2 := User{age: 20, name: "Suzuki"}
  fmt.Println(user_2) // {Suzuki 20}

  user_3 := User{name: "Suzuki"}
  fmt.Println(user_3) // {Suzuki 0}
}

フィールド名を指定しない場合、構造体すべてのフィールドに対して値をセットする必要があります。
また構造体リテラルに記述するフィールドは、構造体での定義順と合わせる必要があります。

package main

import "fmt"

type User struct {
  name string
  age  int
}

func main() {
  // OK
  user_1 := User{"Suzuki", 20}
  fmt.Println(user_1) // {Suzuki 20}

  // NG(全てのフィールドを定義していないため)
  user_2 := User{"Suzuki"}
  fmt.Println(user_2) //  too few values in User{...}

  // NG(フィールドの記述順が正しくないため)
  user_3 := User{20, "Suzuki"}
  fmt.Println(user_3)
  // cannot use 20 (type untyped int) as type string in field value
  // cannot use "Suzuki" (type untyped string) as type int in field value
}

ポインタ型を使う場合

ポインタ型を使う場合の構造体の初期化方法について説明します。

アドレス演算子と構造体リテラルを利用する方法

アドレス演算子(&)と構造体リテラルを利用して&{}という形で構造体の初期化をする方法です。
構造体リテラルを利用しているため、1行で構造体の初期化ができます。

構造体リテラルの記述方法は、さきほど『構造体リテラルでフィールド値をセットする方法』の項目で紹介したルールと同じです。

package main

import "fmt"

type User struct {
  name string
  age  int
}

func main() {

  user_1 := &User{name: "Suzuki", age: 20}
  fmt.Println(user_1) // &{Suzuki 20}

  user_2 := &User{age: 20, name: "Suzuki"}
  fmt.Println(user_2) // &{Suzuki 20}

  user_3 := &User{name: "Suzuki"}
  fmt.Println(user_3) // &{Suzuki 0}

  user_4 := &User{"Suzuki", 20}
  fmt.Println(user_4) // &{Suzuki 20}

  // NG(全てのフィールドを定義していないため)
  user_5 := &User{"Suzuki"}
  fmt.Println(user_5) //  too few values in User{...}

また、以下の結果からわかる通り、アドレス演算子を利用して構造体の初期化をした場合、変数の型はポインタ型(*)となります。

package main

import "fmt"

type User struct {
  name string
  age  int
}

func main() {

  // ポインタ型
  user_1 := &User{name: "Suzuki", age: 20}
  fmt.Println(user_1)        // &{Suzuki 20}
  fmt.Printf("%T\n", user_1) // *main.User

// ポインタ型じゃない
  user_2 := User{name: "Sato", age: 20}
  fmt.Println(user_2)        // {Sato 20}
  fmt.Printf("%T\n", user_2) // main.User
}

参考: アドレス演算子を利用した、構造体の初期化メソッド

以下のようにするとアドレス演算子を利用した構造体の初期化をメソッドにできます。

package main

import "fmt"

type User struct {
  name string
  age  int
}

func newUser(name string, age int) *User {
  return &User{name: name, age: age}
}

func main() {
  user := newUser("Suzuki", 20)
  fmt.Println(user) // &{Suzuki 20}
}

参考: ポインタ型の構造体のフィールドへのアクセス方法について

ポインタ型の構造体のフィールドへアクセスする場合(*user).nameのように記述します。
しかしGo言語ではuser.nameとも記述可能です。1

package main

import "fmt"

type User struct {
  name string
  age  int
}

func main() {

  user := &User{name: "Suzuki", age: 20}

  fmt.Println((*user).name) // Suzuki
  fmt.Println(user.name)    // Suzuki
}

newを利用する方法

newを利用してポインタ型で構造体を初期化する方法です。
user := new(User)user := &User{}と同じ意味になります。
つまり、newを利用した場合は『変数定義 → フィールド値のセット』と2段階で初期化することになります。

package main

import "fmt"

type User struct {
  name string
  age  int
}

func main() {

  user_1 := new(User)
  user_1.name = "Suzuki"
  user_1.age = 20
  fmt.Println(user_1) // &{Suzuki 20}

  // NG(newは1行で初期化できないため)
  user_2 := new(User{"Suzuki", 20})
  fmt.Println(user_2) //  User{...} is not a type
}

参考: newを利用した、構造体の初期化メソッド

以下のようにするとnewを利用した構造体の初期化をメソッドにできます。

package main

import "fmt"

type User struct {
  name string
  age  int
}

func newUser(name string, age int) *User {
  user := new(User)
  user.name = name
  user.age = age
  return user
}

func main() {
  user := newUser("Suzuki", 20)
  fmt.Println(user) // &{Suzuki 20}
}

まとめ

構造体の初期化方法まとめ
  • 構造体の変数定義は『s := S{}』『var s S = S{}』『var s S』の3パターン
  • 構造体リテラルでフィールド値をセットできる
  • 構造体リテラルを利用すると1行で構造体の初期化ができる
  • 構造体リテラルでフィールド名を定義しない場合、構造体での定義順ですべてのフィールドをセットする必要がある
  • newやアドレス演算子で構造体を定義した場合、ポインタ型になる

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考