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