コンテンツにスキップ

Go/文法の概要

出典: フリー教科書『ウィキブックス(Wikibooks)』
< 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は型引数で、anyinterface{}の別名で任意の型を意味します。Print関数はどんな型の引数にも対応できるようになります。

型制約

[編集]

Goのジェネリクスでは、型引数に制約を設けて、関数やメソッドが受け入れる型を限定できます。型制約を使うことで、特定のインターフェースを満たす型や特定の型に対してのみ操作を適用できます。

型制約は、interface を使って、特定のメソッドを持つ型に制限したり、特定の型セット(例えば数値型や文字列型)に限定することができます。

例えば、数値型に限定した型制約は次のように書けます:

type Number interface {
    int | int32 | int64 | float32 | float64
}

func Add[T Number](a, b T) T {
    return a + b
}

上記のコードでは、Number という型制約を使って、intfloat64 などの数値型だけを 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ループがあり、whiledo-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はシンプルで効率的な文法を持ち、特に並行処理やエラーハンドリングに優れた機能を備えています。シンプルで直感的な構造を提供し、学習曲線が低い一方で高いパフォーマンスを実現できます。