LoginSignup
1
0

More than 1 year has passed since last update.

Goの構造体に関してのアウトプット

Last updated at Posted at 2022-08-15

初めに

Goの構造体を勉強したため、アウトプットのため記事を投稿する。

■ 実行環境
The Go Playground
■ GOのバージョン
1.18

概要

構造体とは・・・異なるデータ型を1つにしたもの
1つ1つのデータをフィールド呼ぶ。

構造体の宣言

基本形
type [構造体名] struct {
	[フィールド名] []
}
type Go struct {
	X int
	Y int
	S string
}

フィールドの型が同じ場合は、カンマを使用して同じ列に記載可能である。

type Go struct {
	X, Y int
	S, T string
}

※構造体名の頭文字は小文字でも宣言できるが、その場合は外部のパッケージから参照することができないので、使用用途に合わせて変更する必要がある。
余談だが、Go言語の予約後に「go」という単語があるので、上記例にて頭文字を小文字で使用するとエラーになる。

構造体の初期化

構造体の初期化は、値を指定しない方法と値をあらかじめ指定する方法がある。

値を指定しない方法

値を指定せずに初期する場合は、3つの初期化方法がある。
いずれの場合も同じように初期化されている。
また、初期化したフィールドの変数にはゼロ値が格納されている。

type Go struct {
	X int
	Y int
	S string
}

func main() { 
    // 1つ目
    one := Go{}
    // 2つ目
	var two Go = Go{}
    // 3つ目
	var three Go

	fmt.Println(one)
	fmt.Println(two)
	fmt.Println(three)
}

// 出力結果
{0 0 }
{0 0 }
{0 0 }

値を指定する方法

値を指定する場合は、全てのフィールドの変数に値を代入する方法と、一部の変数に値を代入する方法がある。

全てのフィールドの変数に値を代入して初期化

type Go struct {
	X int
	Y int
	S string
}

func main() {
    all := Go{1, 2, "s"}
	fmt.Println(all)
}

// 出力結果
{1 2 s}

全ての値をセットしていないとエラーになる。

type Go struct {
	X int
	Y int
	S string
}

func main() {
    // 変数Sの値をセットしていない
    all := Go{1, 2}
	fmt.Println(all)
}

// 出力結果
too few values in struct literal

一部のフィールドの変数に値を代入して初期化

一部のフィールドの変数に値を代入して初期化したい場合は、フィールドの変数名を指定して初期化する。
その場合、フィールドを指定していない変数の値はゼロ値となる。

type Go struct {
	X int
	Y int
	S string
}

func main() {
	part := Go{Y: 3}
	fmt.Println(part)
}

// 出力結果
{0 3 }

ポインタを使用して初期化

初期化する際にポインタを使用することができる。

&演算子

&演算子を初期化時に使用することができる。
下記コードだと、GOという構造体へのポインタであることが分かる。

type Go struct {
	X int
	Y int
}

func main() {
	p := &Go{}
	fmt.Printf("%T\n", p)
}

// 出力結果
*main.Go

new

newを使用しても、上記の&演算子を使用した時と同じように初期化することができる。

type Go struct {
	X int
	Y int
}

func main() {
	p := new(Go)
	fmt.Printf("%T\n", p)
}
// 出力結果
*main.Go

構造体を初期化しないで使用を試みる

初期化を行わないで構造体を使用しようとするとエラーになる。
そのため、構造体は必ず初期化をして使用する。

func main() {

	type Go struct {
		X int
		Y int
		S string
	}
	fmt.Println(Go)
}

// 出力結果
Go (type) is not an expression

構造体へのアクセス

構造体へアクセスするには.を使用する。

type Go struct {
	X int
	Y int
	S string
}

func main() {
	st := Go{1, 2, "initial"}
	fmt.Println(st)
	st.X = 3
	st.S = "second"
	fmt.Println(st)
}

// 出力結果
{1 2 initial}
{3 2 second}

.を使用して代入したフィールドXとSの値のみ変更されていることが分かる。

値渡しと参照渡し

値渡し

type Go struct {
	X int
	Y int
	S string
}

func passValue(pVal Go) {
	// pValを使用して構造体の値を変更
	pVal.X = 2
	pVal.S = "値渡し"
	fmt.Println("仮引数 =", pVal)
}

func main() {
	// 構造体Goの初期化
	p := Go{}
	// 構造体Goを代入した変数pの型を出力
	fmt.Printf("実引数の型 = %T", p)
	// 変数pを出力
	fmt.Println("\n実引数の型 =", p)
	// 変数pを関数に値渡し
	passValue(p)
	// 値渡し後の変数pを出力
	fmt.Println("値渡し後の変数p =", p)
}

// 出力結果
実引数の型 = main.Go
実引数の型 = {0 0 }
仮引数 = {2 0 値渡し}
値渡し後の変数p = {0 0 }

値渡しのため、値渡し後の変数pの値が変化していないことが分かる。

参照渡し

type Go struct {
	X int
	Y int
	S string
}

// 構造体Goへのポインタである変数pを仮引数pRefとして受け取る
func passRef(pRef *Go) {
	fmt.Printf("仮引数のポインタ = %p", pRef)
	// pRefを使用して構造体の値を変更
	pRef.X = 2
	pRef.S = "参照渡し"
}

func main() {
	// 構造体Goの初期化
	p := &Go{}
	// 構造体Goへのポインタである変数pのメモリアドレスを出力
	fmt.Printf("実引数のポインタのメモリアドレス = %p", p)
	// ポインタpの型を出力
	fmt.Printf("\n実引数の型 = %T ", p)
	// ポインタpを出力
	fmt.Println("\n実引数 =", p)
	// 構造体Goへのポインタを関数に参照渡し
	passRef(p)
	// 参照渡し後のポインタpを出力
	fmt.Println("\n参照渡し後の実引数 =", p)
}

// 出力結果
実引数のポインタのメモリアドレス = 0xc00005c020
実引数の型 = *main.Go 
実引数 = &{0 0 }
仮引数のポインタ = 0xc00005c020
参照渡し後の実引数 = &{2 0 参照渡し}

実引数と仮引数のメモリアドレスが同様であり、構造体を参照渡しできることが分かる。

ネスト

構造体はネストして使用できる。
GOはclassが存在しないが、構造体をネストして使用することで、クラスの継承のような機能を享受できる。
今回は、ネストする構造体を「親の構造体」、ネストされる構造体を「子の構造体」という名称で表記する。

通常のネスト

ネストする場合は、親の構造体のフィールドに子の構造体の情報を記載する。

func main() {
	type child struct {
		s string
	}
	type parent struct {
		s     string
		chind child
	}

	// 親の構造体であるparentと子の構造体であるchildを一緒に初期化する
	st := parent{"parent structのfield  ", child{"child structのfield"}}
	// 初期化した親の構造体を出力
	fmt.Println(st)
	// 子の構造体であるchildのフィールドの値を、親の構造体であるparent経由でアクセスして変更する
	st.chind.s = "child structの値を変更します"
	fmt.Println(st)
}

// 出力結果
{parent structのfield   {child structのfield}}
{parent structのfield   {child structの値を変更します}}

子の構造体である、childのsというフィールドの値が変更されていることが分かる。

子の構造体を初期化して変数に代入し、その変数を親の構造体の初期化時に使用することもできる。

func main() {
	type child struct {
		s string
	}
	type parent struct {
		s     string
		chind child
	}
	// 子の構造体であるchildを初期化する
	c := child{"childを初期化します"}
	st := parent{"parent structのfield  ", c}
	fmt.Println(st)
}

// 出力結果
{parent structのfield   {childを初期化します}}

スライス

スライスの要素に構造体を格納することもできる。

func main() {
	var Go = []struct {
		X int
		S string
	}{
		{
			X: 0,
			S: "要素0",
		},
		{
			X: 1,
			S: "要素1",
		},
	}
	fmt.Println(Go)
	Go[0].S = "change"
	fmt.Println(Go)
}

// 出力結果
[{0 要素0} {1 要素1}]
[{0 change} {1 要素1}]

構造体のフィールドにスライスを格納することもできる。

func main() {
	type Go struct {
		X []int
	}
	// 初期化
	st := Go{}
	fmt.Println(st)
	// フィールドXにスライスを代入
	st.X = []int{1, 2, 3}
	fmt.Println(st)
}

// 出力結果
{[]}
{[1 2 3]}

構造体のフィールドに構造体を使用する

構造体のフィールドに構造体を使用することもできる。

type A struct {
	S string
}

type B struct {
	S string
	A A
}

func main() {
	var b B
	fmt.Printf("(%v, %T)\n", b.A, b.A) // ({}, main.A)
	b.A = A{"S"}
	fmt.Printf("(%v, %T)\n", b.A, b.A)     // ({S}, main.A)
	fmt.Printf("(%v, %T)\n", b.A.S, b.A.S) // (S, string)
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0