キーワード

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を活用してみてください。

関連記事

Back to top button
目次へ