Go/文法の概要
Goは、シンプルかつ効率的な文法を特徴とし、構造化プログラムや並行処理に優れた設計がされています。以下にGoの文法の概要を紹介します。
基本構造
編集Goプログラムはpackage
宣言から始まり、メインの実行ファイルの場合はpackage main
と記述します。エントリーポイントはfunc main()
関数です。
- hello.go
package main import "fmt" func main() { fmt.Println("Hello, World!") }
変数
編集変数の宣言にはvar
キーワードを使い、型推論も可能です。また、関数内であれば短縮宣言(:=
)も利用できます。
var x int = 10 var y = "Hello" z := 3.14
定数
編集定数はconst
キーワードで宣言し、再代入ができません。
const Pi = 3.14 const Greeting = "Hello, Go!"
データ型
編集Goには以下のような基本データ型が用意されています。
- 整数型:
int
,int8
,int16
,int32
,int64
- 浮動小数点型:
float32
,float64
- 論理型:
bool
- 文字列型:
string
配列とスライス
編集- 配列:固定長のデータ構造です。
- スライス:可変長で、要素の追加や削除が可能なデータ構造です。
var arr [3]int = [3]int{1, 2, 3} slice := []int{4, 5, 6} slice = append(slice, 7)
マップ
編集マップ(map
)はキーと値のペアでデータを管理します。
dict := map[string]int{"apple": 1, "banana": 2} fmt.Println(dict["apple"])
関数
編集Goでは関数も重要な要素で、複数の戻り値が可能です。また、関数を変数として扱うこともできます。
func add(a int, b int) int { return a + b } func swap(x, y string) (string, string) { return y, x }
構造体
編集Goではクラスはありませんが、構造体(struct
)を使ってデータをカプセル化できます。
type Person struct { Name string Age int } person := Person{Name: "Alice", Age: 30} fmt.Println(person.Name)
メソッド
編集Goでは、構造体に対してメソッドを定義できます。メソッドは、特定の型(構造体)に関連付けられた関数です。メソッドを定義する際には、レシーバー(受け取る変数)を指定します。
func (p Person) Greet() { fmt.Println("Hello, my name is", p.Name) } person := Person{Name: "Alice", Age: 30} person.Greet() // "Hello, my name is Alice"
インターフェース
編集インターフェースは、型が持つべきメソッドのセットを定義します。Goでは、明示的に「実装する」と記述することなく、構造体がインターフェースのメソッドセットを満たすと、自動的にインターフェースを実装したと見なされます。
type Greeter interface { Greet() } func SayHello(g Greeter) { g.Greet() } person := Person{Name: "Alice", Age: 30} SayHello(person) // "Hello, my name is Alice"
型引数
編集Go 1.18以降、ジェネリクス(型引数)がサポートされるようになり、関数や構造体に型を引数として渡すことができます。これにより、型に依存しない汎用的なコードを作成できます。
func Print[T any](value T) { fmt.Println(value) } Print(42) // 42 Print("Hello") // Hello
上記の例では、T
は型引数で、any
はinterface{}
の別名で任意の型を意味します。Print
関数はどんな型の引数にも対応できるようになります。
型制約
編集Goのジェネリクスでは、型引数に制約を設けて、関数やメソッドが受け入れる型を限定できます。型制約を使うことで、特定のインターフェースを満たす型や特定の型に対してのみ操作を適用できます。
型制約は、interface
を使って、特定のメソッドを持つ型に制限したり、特定の型セット(例えば数値型や文字列型)に限定することができます。
例えば、数値型に限定した型制約は次のように書けます:
type Number interface { int | int32 | int64 | float32 | float64 } func Add[T Number](a, b T) T { return a + b }
上記のコードでは、Number
という型制約を使って、int
や float64
などの数値型だけを Add
関数に渡すことができるようにしています。このように、型制約を使うことで型の安全性を高め、汎用的な関数を作成できます。
ポインタ
編集Goではポインタを使って変数のアドレスを参照することができますが、ポインタ演算はサポートされていません。
var a int = 10 var p *int = &a fmt.Println(*p) // 10
条件分岐
編集Goではif
文とswitch
文が使えます。条件式の前に短いステートメントを記述することも可能です。
if x := 10; x > 5 { fmt.Println("x is greater than 5") } switch day := "Monday"; day { case "Monday": fmt.Println("Start of the week") case "Friday": fmt.Println("End of the week") default: fmt.Println("Midweek") }
ループ
編集Goにはfor
ループがあり、while
やdo-while
の代わりに使います。
for i := 0; i < 10; i++ { fmt.Println(i) } i := 0 for i < 10 { // while like fmt.Println(i) i++ } i := 0 for { // for ever fmt.Println(i) i++ if i >= 10 { break } } // スライスやマップの要素を反復処理 for index, value := range slice { fmt.Println(index, value) }
ゴルーチン
編集Goではgoroutine
を使って並行処理を行います。go
キーワードで関数を非同期に実行できます。
func sayHello() { fmt.Println("Hello") } go sayHello()
チャネル
編集チャネルはゴルーチン間でデータをやり取りするために使われます。chan
キーワードを使ってチャネルを宣言します。
messages := make(chan string) go func() { messages <- "ping" }() msg := <-messages fmt.Println(msg)
エラーハンドリング
編集Goではエラーはerror
型の戻り値として処理します。エラーチェックは一般的に以下のように行います。
func divide(a, b int) (int, error) { if b == 0 { return 0, fmt.Errorf("division by zero") } return a / b, nil } result, err := divide(10, 0) if err != nil { fmt.Println(err) } else { fmt.Println(result) }
defer文
編集defer
文は、関数が終了する際に指定した関数を遅延実行するために使います。主にリソースの解放やクリーンアップ処理で利用され、エラーが発生しても確実に実行されます。
func main() { defer fmt.Println("End") // 関数の終了時に実行される fmt.Println("Start") }
- 出力結果:
Start End
複数のdefer
がある場合は、後から指定した順に実行されます(LIFO順序)。
パッケージ
編集Goではimport
を使って標準ライブラリや外部パッケージをインポートできます。
import ( "fmt" "math" ) func main() { fmt.Println(math.Sqrt(16)) }
まとめ
編集Goはシンプルで効率的な文法を持ち、特に並行処理やエラーハンドリングに優れた機能を備えています。シンプルで直感的な構造を提供し、学習曲線が低い一方で高いパフォーマンスを実現できます。