Go言語のstructの基本と使い方について解説
Go言語のstructはデータをまとめるシンプルな要素です。
この記事では、設定済みの開発環境を前提に、基本的な使い方や実用的な利用例を簡潔に紹介します。
Go初心者から経験者まで、すぐに試せる情報に注目してください。
基本
structの定義と役割
Go言語におけるstructは、複数の値をひとまとめに管理するためのデータ型です。
structを定義することで、論理的に関連する値をまとまりとして扱え、コードの可読性や保守性を向上させます。
具体的な用途としては、オブジェクト指向プログラミングのような形で、データとその動作をひとつの単位にまとめることが挙げられます。
型とフィールドの関係
structは複数のフィールドを持ち、それぞれのフィールドに固有の型を指定できます。
フィールドは、数値、文字列、配列、または他のstructなど、様々な型の値を保持できます。
これにより、複雑なデータ構造をシンプルに定義できる点が特徴です。
宣言と定義の書式
型定義の基本構文
structの型を定義する基本的な構文は以下のようになります。
以下のサンプルコードは、Personという名前のstructを定義する例です。
package main
import "fmt"
// Person構造体は名前と年齢を保持する
type Person struct {
	Name string // 名前
	Age  int    // 年齢
}
func main() {
	// Person型の変数を生成して出力
	p := Person{Name: "太郎", Age: 30}
	fmt.Println(p)
}{太郎 30}フィールドの設定方法
structの各フィールドには、型に応じた値を代入することが必要です。
フィールドは変数や定数と同様に利用できます。
以下の例では、Book構造体を定義し、フィールドに文字列型と整数型を使用しています。
package main
import "fmt"
// Book構造体は書籍のタイトルとページ数を持つ
type Book struct {
	Title string // 書籍タイトル
	Pages int    // ページ数
}
func main() {
	// Book型の変数を名前付きフィールドで初期化
	b := Book{
		Title: "Go言語入門",
		Pages: 250,
	}
	fmt.Println(b)
}{Go言語入門 250}タグ記述の活用例
構造体のフィールドにはタグを付加でき、JSONとの連携やデータベースとのマッピングなどに利用されます。
以下の例では、JSON連携を意識したタグを設定しています。
package main
import (
	"encoding/json"
	"fmt"
)
// User構造体はJSON変換用のタグを持つ
type User struct {
	Username string `json:"username"` // JSONのキー「username」に対応
	Email    string `json:"email"`    // JSONのキー「email」に対応
}
func main() {
	user := User{Username: "sampleUser", Email: "user@example.com"}
	// User構造体をJSON文字列に変換
	data, _ := json.Marshal(user)
	fmt.Println(string(data))
}{"username":"sampleUser","email":"user@example.com"}インスタンス生成と初期化
リテラルによる生成方法
structのインスタンスはリテラル記法で生成できます。
リテラルによる生成は手軽で、フィールドに初期値を設定しながら生成するのに適しています。
名前付きフィールドと順序フィールドの違い
リテラルを用いる際、フィールドを名前付き指定する方法と、順序に従って指定する方法があります。
名前付き指定はコードの可読性が高く、順序指定は短く記述できる点が特徴です。
名前付きフィールドを利用した例
package main
import "fmt"
// Car構造体は車の情報を持つ
type Car struct {
	Brand string
	Year  int
}
func main() {
	// 名前付きフィールドで初期化
	c1 := Car{
		Brand: "Toyota",
		Year:  2020,
	}
	fmt.Println(c1)
}{Toyota 2020}順序フィールドを利用した例
package main
import "fmt"
// Car構造体の順序指定による初期化
type Car struct {
	Brand string
	Year  int
}
func main() {
	// 順序指定で初期化(フィールドの順序に依存)
	c2 := Car{"Honda", 2018}
	fmt.Println(c2)
}{Honda 2018}new関数を利用した生成方法
標準のnew関数を使うと、構造体のポインタを生成できます。
new関数で生成した場合、全てのフィールドは型ごとのゼロ値で初期化されます。
package main
import "fmt"
// Student構造体は学生の情報を持つ
type Student struct {
	Name string
	Age  int
}
func main() {
	// new関数を用いてStudent型のポインタを生成
	s := new(Student)
	// ゼロ値で初期化される(Nameは""、Ageは0になる)
	fmt.Println(s)
}&{ 0}値とポインタの取り扱い
structの変数は値として扱う場合と、ポインタとして扱う場合があります。
値として渡すとコピーが作成され、ポインタの場合は参照渡しとなるため、変更が呼び出し元に反映されます。
値渡しの例
package main
import "fmt"
type Counter struct {
	Count int
}
func increment(val Counter) {
	val.Count++
}
func main() {
	c := Counter{Count: 5}
	increment(c)
	// コピーされたため、元のc.Countは変更されない
	fmt.Println(c.Count)
}5ポインタ渡しの例
package main
import "fmt"
type Counter struct {
	Count int
}
func increment(val *Counter) {
	val.Count++
}
func main() {
	c := Counter{Count: 5}
	increment(&c)
	// 参照渡しのため、c.Countが変更される
	fmt.Println(c.Count)
}6メソッドの定義と利用
メソッドの基本記述方法
Go言語では、structに対してメソッドを定義できます。
メソッドはレシーバーを持ち、呼び出し側の値またはポインタに対する処理を記述できます。
以下はRectangle構造体に対して面積を計算するメソッドの例です。
package main
import "fmt"
// Rectangle構造体は長方形を表す
type Rectangle struct {
	Width  float64
	Height float64
}
// Areaメソッドは長方形の面積を計算する(値レシーバー)
func (r Rectangle) Area() float64 {
	return r.Width * r.Height
}
func main() {
	rect := Rectangle{Width: 5.0, Height: 3.0}
	// Areaメソッドを利用して面積を計算
	fmt.Println(rect.Area())
}15値レシーバーとポインタレシーバーの使い分け
メソッドのレシーバーは、値レシーバーとポインタレシーバーのどちらかを使用できます。
値レシーバーはコピーされた値を操作し、ポインタレシーバーは元の値に直接作用します。
変更が必要な場合はポインタレシーバーを、参照渡しが不要の場合は値レシーバーを用いるとよいでしょう。
呼び出し時の留意点
値レシーバーとポインタレシーバーは、メソッド呼び出し時に自動で変換されるため、意識せずとも使い分けが行われます。
ただし、構造体が大きい場合には、コピーコストを避けるためにポインタレシーバーを利用するのが一般的です。
値レシーバーの例
package main
import "fmt"
type Number struct {
	Value int
}
// Incrementは値レシーバーを用いて値をインクリメント(コピーで操作)
func (n Number) Increment() {
	n.Value++
}
func main() {
	num := Number{Value: 10}
	num.Increment()
	// コピーのため、元のnum.Valueは変化しない
	fmt.Println(num.Value)
}10ポインタレシーバーの例
package main
import "fmt"
type Number struct {
	Value int
}
// IncrementPointerはポインタレシーバーで直接値を変更
func (n *Number) IncrementPointer() {
	n.Value++
}
func main() {
	num := Number{Value: 10}
	num.IncrementPointer()
	// 直接変更されるため、num.Valueが更新される
	fmt.Println(num.Value)
}11埋め込みと拡張
構造体の埋め込みの基本
Goでは、あるstructに別のstructを埋め込むことで、フィールドやメソッドを継承したように扱うことができます。
これにより、コードの再利用性が向上し、よりシンプルな設計が可能になります。
package main
import "fmt"
// Base構造体は基本情報を保持する
type Base struct {
	ID int
}
// Detail構造体はBase構造体を埋め込み、追加情報を保持する
type Detail struct {
	Base       // Base構造体の埋め込み
	Description string
}
func main() {
	d := Detail{
		Base: Base{
			ID: 1001,
		},
		Description: "詳細情報",
	}
	// 埋め込みによるフィールドアクセスが可能
	fmt.Println(d.ID, d.Description)
}1001 詳細情報フィールドの再利用と拡張方法
埋め込みを利用することで、既存のstructのフィールドやメソッドをそのまま利用でき、必要に応じて新たなフィールドやメソッドを追加することが可能です。
下記の例は、基本的な型に対して拡張を行う方法を示しています。
package main
import "fmt"
// Vehicle構造体は共通フィールドを保持する
type Vehicle struct {
	Make  string
	Model string
}
// Car構造体はVehicleを埋め込み、さらに特有のフィールドを追加
type Car struct {
	Vehicle // Vehicle構造体の埋め込み
	Doors   int // ドア数
}
func main() {
	car := Car{
		Vehicle: Vehicle{
			Make:  "Ford",
			Model: "Mustang",
		},
		Doors: 2,
	}
	// 埋め込みによるVehicleフィールドと、Car独自のフィールドを利用
	fmt.Println(car.Make, car.Model, car.Doors)
}Ford Mustang 2活用例
JSON連携におけるタグ活用
structのタグ情報を活用すると、JSONとのデータの変換がスムーズに行えます。
タグを設定することで、JSONデータとGoのフィールド名とを柔軟にマッピングできます。
package main
import (
	"encoding/json"
	"fmt"
)
// Product構造体はJSONのキーとフィールドをマッピングするタグを持つ
type Product struct {
	Name  string  `json:"name"`
	Price float64 `json:"price"`
}
func main() {
	// JSONデータを生成
	jsonStr := `{"name": "Laptop", "price": 1200.50}`
	var p Product
	// JSONのパース
	_ = json.Unmarshal([]byte(jsonStr), &p)
	fmt.Println(p)
}{Laptop 1200.5}複数構造体を組み合わせた実装例
複数のstructを組み合わせることで、複雑なデータ構造を管理することができます。
以下は、複数の構造体を埋め込みやフィールドとして使用し、実際のデータを扱う例です。
package main
import "fmt"
// Address構造体は住所情報を保持する
type Address struct {
	City    string
	Country string
}
// User構造体は住所情報をフィールドとして持つ
type User struct {
	Name    string
	Age     int
	Address Address
}
func main() {
	user := User{
		Name: "Alice",
		Age:  28,
		Address: Address{
			City:    "Tokyo",
			Country: "Japan",
		},
	}
	fmt.Println(user)
}{Alice 28 {Tokyo Japan}}まとめ
この記事では、Go言語のstructの定義から生成、初期化、メソッド定義、埋め込みの活用まで、基本的な使い方とその応用例を解説しました。
各セクションで具体的なコード例と留意点を提示しており、実際のプロジェクトでの効率的なデータ構造の実装方法が理解できる内容となっております。
ぜひ、この記事を参考にご自身のプロジェクトでstructを活用してみてください。