Go

Go入門

小さなプログラムを実行し、結果を確かめながらGoの基礎を学びましょう。

この講座はGo 1.22以降を対象にしています。

01

Goを実行してみよう

Goはコンパイルが速く、Web APIやコマンドラインツールなどに使われる言語です。公式ディストリビューションを導入し、go versionで確認します。

main.goGO
package main

import "fmt"

func main() {
    fmt.Println("こんにちは、Go!")
}
実行COMMAND
go run main.go
実行結果OUTPUT
こんにちは、Go!

package mainfunc main()が実行開始点です。保存後はgofmt -w main.goで自動整形できます。

02

変数とデータ型

varは型を明示し、:=は右辺から型を推論します。:=は関数内でのみ使えます。

主な型GO
name := "Mity"       // string
var age int = 20      // int
height := 170.5       // float64
isStudent := true     // bool

fmt.Printf("%sさん %d歳 %.1fcm %t\n", name, age, height, isStudent)
実行結果OUTPUT
Mityさん 20歳 170.5cm true
未初期化の値 intは0、stringは空文字列、boolはfalseに自動初期化されます。使わない変数やimportはコンパイルエラーになります。
03

入力と型変換

fmt.Scanはキーボード入力を変数へ読み込みます。戻り値のエラーも確認しましょう。

年齢を受け取るGO
var age int
fmt.Print("年齢: ")
if _, err := fmt.Scan(&age); err != nil {
    fmt.Println("整数で入力してください")
    return
}
fmt.Printf("来年は%d歳です\n", age+1)
実行例OUTPUT
年齢: 20
来年は21歳です
04

演算子と条件分岐

比較は== != < > <= >=、論理演算は&& || !を使います。条件の括弧は不要です。

点数を判定GO
score := 82
if score >= 80 {
    fmt.Println("合格(よくできました)")
} else if score >= 60 {
    fmt.Println("合格")
} else {
    fmt.Println("再挑戦")
}
実行結果OUTPUT
合格(よくできました)
05

繰り返し

Goの繰り返しはfor一種類です。初期化、条件、更新をセミコロンで区切ります。

1から5までGO
for i := 1; i <= 5; i++ {
    fmt.Printf("%d回目\n", i)
}
実行結果OUTPUT
1回目
2回目
3回目
4回目
5回目
無限ループ for { }は条件がないため、breakなどで終了させない限り続きます。
06

スライスとマップ

スライスは可変長の並び、マップはキーと値の組です。

値を追加して取り出すGO
fruits := []string{"りんご", "みかん"}
fruits = append(fruits, "ぶどう")
scores := map[string]int{"Aki": 82, "Mika": 91}

for i, fruit := range fruits {
    fmt.Printf("%d: %s\n", i+1, fruit)
}
fmt.Printf("Mika: %d点\n", scores["Mika"])
if _, ok := scores["Ken"]; !ok {
    fmt.Println("Ken: 未登録")
}
実行結果OUTPUT
1: りんご
2: みかん
3: ぶどう
Mika: 91点
Ken: 未登録

マップからは値と存在を表すokを同時に受け取れます。上の例は値を使わないため_で破棄し、okがfalseのときに未登録と表示しています。値も必要ならvalue, ok := scores["Ken"]と書きます。

07

関数

関数は処理をまとめ、引数を受け取って結果を返します。

合計を返すGO
func total(price, count int) int {
    return price * count
}

func main() {
    fmt.Printf("合計: %d円\n", total(500, 3))
}
実行結果OUTPUT
合計: 1500円
ポイント
  • 関数名は役割が伝わる名前にする
  • 表示と計算を分ける
  • 異常を返すときは値とerrorの2値を返す
08

エラーを値として扱う

Goでは想定可能な失敗をerrorで返し、呼び出し側ですぐ確認します。

安全な割り算GO
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("0では割れません")
    }
    return a / b, nil
}

answer, err := divide(10, 0)
if err != nil {
    fmt.Println(err)
} else {
    fmt.Println(answer)
}
実行結果OUTPUT
0では割れません

この例ではerrorsパッケージのimportが必要です。panicは回復不能な状態に限り、通常の入力ミスには使いません。

09

ファイルの読み書き

os.WriteFileos.ReadFileで小さなファイルを扱えます。処理ごとにエラーを確認します。

保存して読むGO
if err := os.WriteFile("memo.txt", []byte("Goを学習中\n"), 0644); err != nil {
    log.Fatal(err)
}
data, err := os.ReadFile("memo.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Print(string(data))
実行結果OUTPUT
Goを学習中

oslogをimportします。利用者入力をそのままファイル名にせず、重要なファイルの上書きに注意してください。

10

構造体とメソッド

構造体は関連する値をまとめた型です。メソッドはレシーバーを持つ関数です。

商品型GO
type Product struct {
    Name  string
    Price int
}

func (p Product) Label() string {
    return fmt.Sprintf("%s: %d円", p.Name, p.Price)
}

product := Product{Name: "ノート", Price: 180}
fmt.Println(product.Label())
実行結果OUTPUT
ノート: 180円
11

モジュールとテスト

プロジェクトのフォルダーでモジュールを初期化し、整形、テスト、ビルドを実行します。

基本コマンドCOMMAND
go mod init example.com/hello
go fmt ./...
go test ./...
go build
依存関係 外部パッケージを追加した後はgo mod tidygo.modgo.sumを整えます。出所とライセンスも確認しましょう。
PRACTICE

ミニ課題:買い物合計

商品名、単価、個数を持つProduct構造体を作り、スライス内の合計額を関数で計算してください。

完成条件
  • 2商品以上の名前と小計を表示する
  • 個数が1未満ならerrorを返す
  • 想定した実行結果と一致するか確認する
NEXT

次に学ぶこと

基礎を終えたら、次のテーマを小さなプログラムで一つずつ試しましょう。

Goの発展テーマ
  • goroutineとchannel:複数の処理を協調させる並行処理
  • context:タイムアウトやキャンセルを処理間で伝える
  • net/http:標準ライブラリでWebサーバーやAPIを作る
  • testing:テーブル駆動テスト、サブテスト、ベンチマークを学ぶ

最初の発展課題として、net/httpで一つのURLにJSONを返すAPIを作り、testingでレスポンスを確認すると、基礎の関数・構造体・エラー処理を組み合わせられます。