Goの基礎

2026年8月2日

目次

Goとは

Go は、Python のように手軽に使えて、Python よりも高速にかつ安全に動くことを意図された言語のようだが、妙に古臭いというか、懐かしい感じのする言語である。“Go” だと一般的すぎるので、“Go 言語” とか “Golang” と表記されることがある。コンパイラ言語である。

インストール

こちら からダウンロードして設置する。

$ tar xvzf go1.26.5.linux-amd64.tar.gz -C ~/opt

~/.bashrc

export PATH=$PATH:~/opt/go/bin

プログラムの構造と実行

プログラムの基本的な構造は以下のとおりである。

hello.go

package main

import "fmt"

func main() {
    fmt.Println("Hello, world!")
}

ファイルの拡張子は .go である。package の行はおまじないである。import の行はパッケージのインポートである。それに続いて main 関数が定義されている。ここがプログラムの入口になる。コードは少しC言語に似ているが、文末のセミコロンはいらない。というか、内部で自動的に追加される。{ は関数名の下には置けない。というのも、改行でセミコロンが追加されるためである。インデントは公式には Tab である(空白でも動くが)。整形用のコマンドがあるので、あまり細かいことは気にしなくてよい。

$ go fmt hello.go

go vet で問題のある箇所を指摘してくれる。

$ go vet hello.go

実行は、go run で行う。

$ go run hello.go
Hello, world!

go build で実行ファイルができる。

$ go build hello.go
$ ./hello

文

文はセミコロン(;)で終わる。といっても、ある規則で勝手に挿入されるので、基本的には自分では書かない。JavaScript のように、自動で入るには入るが危なっかしいので必ず入れる、ということもなく、入れないのが普通のようである。ただ、変なところで改行したら罠にハマりうるのは JavaScript と同様である。

一見、以下のように書けそうではある。

func main()
{
    fmt.Println("Hello, world!")
}

だが、次のようなエラーが出る。

./hello.go:6:1: syntax error: unexpected semicolon or newline before {

これは、")" のあとに改行が入るとセミコロンが挿入されるためである。変な感じがするかもしれないが、スタイルの揺れを制限しているのである。

コメント

C/C++ などと同様、/* ... */ や // ... でコメントを入れることができる。

/* 文字列を表示 */
fmt.Println("Hello, world!")

変数の定義

変数の定義はつぎのようにする。

var a = 1

型を直接指定することもできる。

var a int
a = 1

あるいは

var a int = 1

Go では型を後につけるのが特徴。

定数にする場合は、const を使う。

const a int = 1

短縮代入 := も使える。

a := 1

これは var a = 1 と同じ意味。左辺の型は推論される。

データ型

Go においては、すべてのデータに明確な型がある。型を調べるには fmt.Printf の %T フォーマットを使える。

fmt.Printf("%T\n", a)

整数型

整数型には int64、uint64 などがある。uint のほうは符号なしである。64 はビット数を表し、8、16、32、64 とある。単に int と書くと、CPU に応じて int64 あるいは int32 となる。uint も同様。値を直接指定する場合(リテラル)の整数型は int である。

var a = 1

byte 型というものがあるが、int8 の別名である。

C/C++ と似ているが、Go では int のサイズがプラットフォーム依存である点に注意。C/C++ では int は通常 32 ビットだが、Go では 64 ビット環境では int64 になる。

浮動小数点数型

浮動小数点数型は、float64、float32 がある。リテラルの型は float64 である。

var a = 1.0

型変換は、型の名前で行える。

a = float64(1)

C/C++ と異なり、暗黙の型変換はほとんど行わない。次のようにするとエラーになる。

var a = 1
a = 1.2  // error

明示的なキャストが必要。

複素数型

複素数型として complex128、complex64 がある。前者が float64、後者が float32 を実部と虚部の表現に使っている。虚数を表すには i を用いる。

var a = 1.0 + 2.0i
fmt.Println(a) // (1+2i)

あるいは、以下のようにしてもよい。

var a = complex128(1.0, 2.0)

real()、imag() で実部と虚部が得られる。

fmt.Println(real(a)) // 1
fmt.Println(imag(a)) // 2

文字列型

文字列は string 型で表す。"..." あるいは '...' ではなく、"..." のみ。

var s string = "Hello"

文字列は不変(immutable)である。一度作成された文字列の内容は変更できない。C の char 配列とは根本的に異なる。

各文字のアクセス

文字列の各文字にアクセスするには、添字を使う(0 から始まる)。

var s string = "Hello, world."
fmt.Printf("%c\n", s[0]) // H

各文字は変更できない。

s[0] = 'h'  // error

添字には負の値は使えない。末尾要素にアクセスするには s[len(s)-1] を使う。

var s string = "Hello, world."
fmt.Println(s[len(s)-1]) // .

Pythonの s[-1] のような負のインデックスはGoにはない。

部分文字列にアクセスすることもできる。1:4 なら 2 文字目から 4 文字目になる(1:4 は添字としては 1, 2, 3 を意味する)。これをスライス表記という。

var s string = "Hello, world."
fmt.Println(s[1:4]) // ell

文字列の長さ

文字列の長さを知るには len() を用いる。

var s string = "Hello, world."
fmt.Println(len(s)) // 13

文字列の連結

文字列の連結には + を用いる。

var s string = "Hello" + ", " + "world" + "."

変数の埋め込み

fmt.Sprintf で変数を埋め込むことができる。

var name string = "world"
var s string = fmt.Sprintf("Hello, %s.", name)

三重引用符に相当するもの

Go には Python の """...""" に相当するものはないが、バッククォート `...` で囲むと、エスケープなしの文字列(raw string)になる。

var s string = `Hello,
world.`

改行を含む文字列をそのまま書ける。

論理型

bool 型があり、true と false を取る。

var b bool = true
fmt.Printf("%T\n", b) // bool

C/C++ では 0 と非 0 が真偽値として使われるが、Go では bool 型が明示的に必要。

ポインタ

ポインタの基本

Go にもポインタがある。C/C++ と同様に & でアドレスを取得し、* で間接参照する。

x := 42
p := &x    // p は x のアドレスを指す
fmt.Println(*p) // 42
*p = 100   // x の値を変更
fmt.Println(x)  // 100

C/C++ と似ているが、Go のポインタはNULL がない。ゼロ値は nil。

var p *int
fmt.Println(p) // <nil>

C/C++との違い

C/C++ ではポインタ演算(p++ など)が可能だが、Go ではポインタ演算はサポートされていない。

また、Go のガベージコレクションにより、ポインタが指す先が寿命を越えることはない(ポインタが指す先が不要になれば GC により回収される)。C/C++ のような「ポインタが指す先が解放された後にアクセスする」ようなバグは起こりにくい。

配列とスライス

配列

var a [3]int
a[0] = 1
a[1] = 2
a[2] = 3

C/C++ の配列に似ているが、Go の配列は値型である。配列を関数に渡すとコピーされる。C/C++ では配列はポインタとして振る舞うが、Go ではそうではない。

同じ型の配列を初期化するには:

var a [3]int = [3]int{1, 2, 3}

長さだけを指定してゼロ値で埋めることもできる。

var a [3]int = [3]int{}

末尾要素のアクセス

Goには負のインデックスがない。末尾要素にアクセスするには a[len(a)-1] を使う。

スライス

配列に対して範囲指定(a[1:3])すると、スライスが得られる。スライスは配列の連続した範囲を指すビューで、要素の追加・削除ができる。内部では配列へのポインタ・長さ・容量の3つの情報を持つ。スライスを定義するには、[]型名で表す。

var a [5]int = [5]int{1, 2, 3, 4, 5}
var s []int = a[1:3]  // スライス
fmt.Println(s) // [2 3]

a[2:]、a[:3]、a[:]のような書き方もできる。

Python の変数がオブジェクトの参照を持つのと違って、Go の変数はオブジェクトそのものを持つ箱である。スライスで配列のビューを実現することによって、Python のリスト型変数(参照)と同じ振る舞いを実現していると考えることができる。

スライスの内部構造

スライス自体はメモリを持っていない。あくまで配列へのポインタ+長さ・容量のヘッダで、実データは配列にある。

スライス s → [ ptr→配列本体, len=3, cap=5 ]

make でスライスを作成する際も、裏で配列が確保されている。

var s []int = make([]int, 3, 5)  // 長さ3、容量5
s[0] = 1
s[1] = 2
s[2] = 3

第3引数(容量)は省略可能。容量を省略すると容量=長さになる。

スライスのスライス(部分スライス)も扱える。

var s []int = []int{1, 2, 3, 4, 5}
fmt.Println(s[1:3]) // [2 3]

注意: スライスの値を変更すると、もとの配列や同じ配列を参照しているスライスの値も変わる(Python のリスト操作ではおなじみの現象)。

a := [5]int{1, 2, 3, 4, 5}
s := a[0:3]  // [1, 2, 3]
s[0] = 99    // a[0]も変わる

append

スライスは可変長なので、append で要素を追加できる。

s := append(s, 4)
fmt.Println(s) // [1 2 3 4]

複数の要素を追加することもできる。

s = append(s, 5, 6, 7)

append で容量を超えると新しい配列が割り当てられる。元の配列とのビュー関係は切れるので、同じ配列を共有しているスライスをappendする場合には気をつける必要がある。

a := make([]int, 3) // cap=3
s1 := a[0:3]           // [0, 0, 0]
s2 := a[1:3]           // [0, 0]

s1 = append(s1, 99) // cap超過で新配列が確保される
s2[0] = 88          // s2はaを参照

fmt.Println(s1) // [0, 0, 0, 99]  ← 新配列
fmt.Println(s2) // [88, 0]        ← aが変更されている
fmt.Println(a)  // [0, 88, 0]

コピー

スライスをコピーするには copy を使う。

src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)

Python の list.copy() やスライス s[:] に相当する。

マップ

mapの定義

m := make(map[string]int)
m["a"] = 1
m["b"] = 2
fmt.Println(m["a"]) // 1

Python の辞書(dict)に相当する。

mapの操作

存在しないキーにアクセスするとゼロ値が返る。

fmt.Println(m["c"]) // 0

キーが存在するか確認するには、代入付きのアクセスを使う。

v, ok := m["c"]
fmt.Println(v, ok) // 0 false

Python で dict.get(key, default) のようにデフォルト値を指定する必要があるが、Go ではこのパターンが標準。

削除は delete を使う。

delete(m, "a")

構造体

structの定義

type Person struct {
    name string
    age  int
}

p := Person{name: "Alice", age: 30}
fmt.Println(p.name) // Alice

C/C++ の struct に似ているが、Go の構造体にはメソッドを定義できる。

フィールドアクセス

Go では . でフィールドにアクセスする

p := Person{name: "Alice", age: 30}
fmt.Println(p.name) // Alice

ポインタ経由の場合も . でアクセスできる((*p).name ではなく p.name と書ける)。

式と演算子

算術演算子

整数・浮動小数点数については、以下のような演算子が使える。

演算子 例 意味
+ a + b 加算
- a - b 減算
* a * b 乗算
/ a / b 除算
% a % b 剰余
- -a 負符号

** のような累乗演算子はない。math.Pow を使う。

import "math"
fmt.Println(math.Pow(2, 10)) // 1024

整数の除算は整数値になる(小数部は切り捨てられる)。

fmt.Println(7 / 2) // 3

小数で除算するには、明示的に浮動小数点数に変換する。

fmt.Println(float64(7) / float64(2)) // 3.5

Pythonの // のような整数除算は、Goでは整数型同士の / に相当する。

代入演算子

+=、-=、*=、/=、%= などの代入演算子がある。

i := 0
i += 1  // i = i + 1 と同じ
i -= 2  // i = i - 2 と同じ
i *= 3  // i = i * 3 と同じ

インクリメント・デクリメント

Goには(Pythonと違って) ++ と -- がある。

i := 0
i++   // i = 1
i--   // i = 0

前置も可能。

fmt.Println(++i)  // iをインクリメントしてから出力

ただし、式の一部としては使えない。

fmt.Println(i++)  // error

これは「文としてのみ有効」というGoの仕様である。Pythonの i += 1 に相当するが、Goでは += ではなく ++ も使える。

ビット演算子

演算子 意味
& AND
\| OR
^ XOR
&^ AND NOT
<< 左シフト
>> 右シフト
fmt.Println(1 & 1)    // 1
fmt.Println(1 | 2)    // 3
fmt.Println(1 ^ 2)    // 3
fmt.Println(1 << 2)   // 4 (1 * 2^2)
fmt.Println(4 >> 1)   // 2 (4 / 2^1)

&^(AND NOT)は、右辺のビットを立てた部分を左辺から取り除く。

fmt.Println(7 &^ 1)   // 6 (111 &^ 001 = 110)

比較演算子

演算子 意味
== 等しい
!= 等しくない
< より小さい
<= 以下
> より大きい
>= 以上
fmt.Println(1 == 1)  // true
fmt.Println(1 != 2)  // true
fmt.Println(1 < 2)   // true

Pythonとの違い: Goでは比較演算子をつなげられない。

// Python: 0 <= x <= 1 は有効
// Go: これはエラー
0 <= x && x <= 1  // こう書く

論理演算子

演算子 意味
&& AND
\|\| OR
! NOT
if x >= 0 && y <= 5 {
    // ...
}

if !found {
    // ...
}

短絡評価(ショートサーキット)が行われる。&& で左が false なら右は評価されない。|| で左が true なら右は評価されない。

// x が 0 より小さい場合、y は評価されない
if x >= 0 && y <= 5 {
    // ...
}

演算子の優先順位

優先順位は以下のとおり(上が高い)。

優先度 演算子
1 ++ --
2 * / % << >> & &^
3 + - \| ^
4 == != < <= > >=
5 &&
6 \|\|

() で囲むことで優先順位を変更できる。

fmt.Println(2 + 3 * 4)  // 14
fmt.Println((2 + 3) * 4) // 20

制御文

if文

if x > 0 {
    fmt.Println("positive")
} else if x < 0 {
    fmt.Println("negative")
} else {
    fmt.Println("zero")
}

C/C++ と似ているが、条件式が必ず bool 型である点に注意。また、{ は改行してはいけない。

for文

Go には while や do-while はない。すべて for で行う。

// C の for ループに相当
for i := 0; i < 10; i++ {
    fmt.Println(i)
}

// while に相当
for i < 10 {
    fmt.Println(i)
    i++
}

// 無限ループ
for {
    // ...
}

range

配列やスライスの各要素をイテレートするには range を使う。

nums := []int{1, 2, 3, 4, 5}
for i, v := range nums {
    fmt.Println(i, v)
}

Python の for i, v in enumerate(nums): に似ている。インデックスだけ欲しい場合は次のようにする。

for i := range nums {
    fmt.Println(i)
}

値だけ欲しい場合は _ でインデックスを無視できる。

for _, v := range nums {
    fmt.Println(v)
}

マップでも使える。

m := map[string]int{"a": 1, "b": 2}
for k, v := range m {
    fmt.Println(k, v)
}

switch文

switch x {
case 1:
    fmt.Println("one")
case 2:
    fmt.Println("two")
default:
    fmt.Println("other")
}

C/C++ の switch と似ているが、各 case で自動的に break されるのが大きな違いである。

明示的にフォールスルーしたい場合は fallthrough を使う。

switch x {
case 1:
    fmt.Println("one")
    fallthrough
case 2:
    fmt.Println("two")
}

関数

関数の定義

func add(a int, b int) int {
    return a + b
}

C/C++ と似ているが、型名が変数名の後ろにくるのが特徴。

同じ型の引数が連続している場合、最後の型名だけを共有できる。

func add(a, b int) int {
    return a + b
}

Go では関数内に関数を定義することができない(無名関数で似たようなことはできるが)。

複数戻り値

Go では複数の値を戻り値にできる。

func divmod(a, b int) (int, int) {
    return a / b, a % b
}

func main() {
    q, r := divmod(10, 3)
    fmt.Println(q, r) // 3 1
}

関数呼び出し

関数は () で呼び出す。

result := add(1, 2)

戻り値が複数の場合は、すべて受け取るか、_ で無視できる。

q, _ := divmod(10, 3)  // r を無視
_, r := divmod(10, 3)  // q を無視

引数渡し

Goの関数引数はすべて値渡しである。引数に渡された値はコピーされる。

func inc(x int) {
    x++  // コピーされたものを変えるだけ
}

func main() {
    v := 1
    inc(v)
    fmt.Println(v) // 1 ← 変わっていない
}

実引数の値を変更したい場合は、ポインタで渡す。

func inc(x *int) {
    *x++  // ポインタ経由で実物を変える
}

func main() {
    v := 1
    inc(&v)
    fmt.Println(v) // 2 ← 変わっている
}

Pythonではミュータブルなオブジェクト(リストなど)を渡すと(参照なので)関数内で変更できるが、Goでは明示的にポインタで渡す必要がある。

可変長引数

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

fmt.Println(sum(1, 2, 3)) // 6

再帰呼び出し

関数の再帰呼び出しはふつうにできる。

func fibonacci(n int) int {
    if n == 0 {
        return 0
    } else if n == 1 {
        return 1
    } else {
        return fibonacci(n - 2) + fibonacci(n - 1)
    }
}

無名関数(ラムダ式)

Goには無名関数(ラムダ式)がある。関数を変数に代入したり、引数に渡したりできる。

f := func() string {
    return "hello"
}
fmt.Println(f()) // hello

関数も値なので、変数に代入できる。引数や戻り値にもできる。

即時実行

result := func() int {
    return 42
}()
fmt.Println(result) // 42

クロージャ

外の変数にアクセスできる(クロージャ)。

i := 0
inc := func() {
    i++  // 外の変数にアクセスできる
}
inc()
fmt.Println(i) // 1

呼ぶたびに 1 増えた数値を返す関数を定義したい。

fmt.Println(f()) // 1
fmt.Println(f()) // 2
fmt.Println(f()) // 3

そのためには、ローカルの環境に変数を閉じ込める形で関数を定義すればよい。この仕組みをクロージャという。目的の関数は、次のように実現できる。

func setup() func() int {
    x := 0
    f := func() int {
        x++
        return x
    }
    return f
}

func main() {
    f := setup()
    fmt.Println(f()) // 1
    fmt.Println(f()) // 2
    fmt.Println(f()) // 3
}

関数内関数

Goで関数内に関数を定義するには、無名関数として定義する必要がある。

func main() {
    f := func() {
        fmt.Println("hello")
    }
    f()
}

メソッド

Goにはクラスがないが、構造体にメソッドを定義できる。

メソッドの定義

メソッドはレシーバー(受信者)を持つ関数である。レシーバーはメソッドが呼び出される対象。

type Dog struct{}

func (d Dog) Speak() string {  // Dog へのメソッド
    return "wan"
}

func main() {
    d := Dog{}
    d.Speak()  // "wan"
}

func (d Dog) がレシーバーの宣言。d を通じて構造体のフィールドにアクセスできる。

ポインタレシーバー

レシーバーをポインタにすると、構造体を変更できる。

type Counter struct {
    n int
}

func (c *Counter) Inc() {  // ポインタレシーバー
    c.n++  // 実物を変更できる
}

func main() {
    c := Counter{n: 0}
    c.Inc()
    fmt.Println(c.n) // 1
}

値レシーバーだとコピーされたものを変えるだけなので、実物を変更したい場合はポインタレシーバーが必要。

インターフェース

Goのインターフェースは、Javaのインターフェースに近い概念だが、明示的な実装宣言がないのが大きな違い。

インターフェースの定義

インターフェースはメソッドシグネチャの集合体を定義する。

type Speaker interface {
    Speak() string
}

これは「Speak() string を持つもの」という契約を定義している。

Javaのインターフェースとの違い

Javaではクラスがインターフェースを明示的に実装する。

// Java
interface Speaker {  // インターフェースの定義
    String speak();
}

class Dog implements Speaker {  // 明示的に実装
    public String speak() {
        return "wan";
    }
}

Goでは明示的な実装宣言がない。構造体が必要なメソッドを持っていれば自動的にインターフェースを満たす(暗黙的実装)。

type Speaker interface {
    Speak() string
}

type Dog struct{}  // 構造体の定義だけ

func (d Dog) Speak() string {  // メソッドを実装
    return "wan"
}

func main() {
    var s Speaker = Dog{}  // Dog は自動的に Speaker を満たす
    fmt.Println(s.Speak()) // "wan"
}

Javaの class Dog implements Speaker に対応する記述がGoにはない。Dogが Speak() メソッドを持っているだけで、自動的に Speaker を満たすとみなされる。要するにダッグタイピング(アヒルっぽいく振る舞うならアヒルとみなす)である。

型アサーション

インターフェースから元の型を取り出すには型アサーションを使う。

var s Speaker = Dog{}
d := s.(Dog)  // Dog 型として取り出す
fmt.Println(d)

この方法だと、失敗するとパニックになる。

type Cat struct{}

func (c Cat) Speak() string {
    return "nyan"
}

func main() {
    var s Speaker = Cat{}
    fmt.Println(s.Speak()) // "nyan"
    d := s.(Dog)
    fmt.Println(d)
}
panic: interface conversion: main.Speaker is main.Cat, not main.Dog

安全に取り出すにはコンマ付きの構文を使う。

d, ok := s.(Dog)
if ok {
    fmt.Println(d)
}

Pythonでいう isinstance(s, Dog) に相当するが、Goでは型アサーションで値自体を取り出せる点が異なる。

エラー処理

Go には例外の仕組みがない。C/C++ の戻り値でのエラーコードに近いが、Go ではエラーが値として扱われる。

errorインターフェース

Go の error はインターフェースである。

type error interface {
    Error() string
}

errors.New、fmt.Errorf

errors.New で単純なエラーを作成できる。

import "errors"

err := errors.New("division by zero")

fmt.Errorf でフォーマット付きのエラーも作れる。

import "fmt"

err := fmt.Errorf("cannot divide %d by %d", a, b)

カスタムエラー

独自のエラー型を作ることができる。

type DivisionError struct {
    msg string
}

func (e DivisionError) Error() string {
    return e.msg
}

Error() string メソッドを持っていれば、それは error インターフェースを満たす。

関数からのエラー返し

Goでは、関数でエラーが発生した場合はエラーを戻り値として返すのが定石である。

func div(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

func main() {
    result, err := div(10, 0)
    if err != nil {
        fmt.Println("error:", err) // "error: division by zero"
        return
    }
    fmt.Println(result)
}

呼び出し元では if err != nil でエラーチェックを行う。

エラー処理のパターン

result, err := div(10, 0)
if err != nil {
    fmt.Println("error:", err)
    return
}
fmt.Println(result)

C/C++ の if (err != 0) に似ているが、Go ではエラーが値として扱われるため、if err != nil でチェックする。

パッケージ

インポート

import (
    "fmt"
    "math"
)

Python の import に似ているが、Go ではパッケージ内の名前(変数、関数、型など)が大文字始まりでないと外部から使えない。

ここで重要なのは、そのパッケージ内の名前が大文字始まりかどうかである。

package fmt  // パッケージ名は小文字

func Println(a ...interface{}) { ... }  // Println は大文字始まり → エクスポートされる
func println(a ...interface{}) { ... }  // println は小文字始まり → エクスポートされない

エクスポート

Go のパッケージは、大文字始まりの名前しか外部に公開されない。変数、関数、型、構造体フィールドなどすべてに適用される。

// math.go
const Pi = 3.14159  // 公開
const pi = 3.14     // 非公開

func Sqrt(x float64) float64 {  // 公開
    return math.Sqrt(x)
}

func sqrt(x float64) float64 {  // 非公開
    return math.Sqrt(x)
}

Python では __name のように明示的にプライベートを示す必要があるが、Go は大文字=公開、小文字=非公開という大雑把なルールである。

構造体名とフィールド

構造体名も、大文字始まりでないと外部から使えない。

// mymath.go
type Person struct {  // 大文字始まり → 外部から使える
    Name string  // 大文字始まり → 外部からアクセスできる
    age  int     // 小文字始まり → 外部からアクセスできない
}

type person struct {  // 小文字始まり → 外部から使えない
    name string
    age  int
}
// main.go
import "mymath"

p := mymath.Person{Name: "Alice", age: 30}
fmt.Println(p.Name)  // 使える
fmt.Println(p.age)   // エラー: mymath.Person.age は非公開

mainパッケージ

main パッケージの main 関数がプログラムの入口になる。

package main

func main() {
    // ...
}

ゴルーチンとチャンネル

ゴルーチンの基本

Go にはゴルーチン(goroutine)という軽量なスレッドがある。go キーワードで起動する。

func say(s string) {
    for i := 0; i < 5; i++ {
        fmt.Println(s)
    }
}

func main() {
    go say("hello")  // 新しいゴルーチンで実行
    say("world")     // 現在のゴルーチンで実行
}

Python の threading.Thread に似ているが、Go のゴルーチンははるかに軽量である。

チャンネル

ゴルーチン間の通信にはチャンネル(channel)を使う。

ch := make(chan string)  // バッファなしチャンネルの作成

// ゴルーチンA:データを送信
go func() {
    ch <- "hello"  // チャンネルにデータを入れる(受信されるまで待機)
}()

// ゴルーチンB:データを受信
msg := <-ch  // チャンネルからデータを取り出す(受信できるまで待機)
fmt.Println(msg) // hello

容量を指定しない場合は、バッファなしチャンネルになる。この場合、送信と受信が同期される(お互いが待機状態になる)。

バッファありチャンネルも作れる。

ch := make(chan string, 2)  // 容量2のバッファ
ch <- "a"
ch <- "b"
ch <- "c"  // ブロックされる(待機状態になる)

バッファありの場合、容量いっぱいになるまで送信はブロックされない。Python の queue.Queue に似ているが、Go のチャンネルはより低レベルで柔軟。

Goモジュール

go.mod

Go 1.11 以降、モジュールシステムが導入された。go.mod ファイルで依存関係を管理する。

$ go mod init myproject

依存管理

依存の追加は go get で行う。

$ go get github.com/some/package

Python の pip install に相当するが、Go のモジュールはバージョン管理がより厳密。

テスト

Go には標準のテストフレームワーク testing がある。これを用いるには go.mod が必要。

// hello_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
    result := add(1, 2)
    if result != 3 {
        t.Errorf("expected 3, got %d", result)
    }
}

Go のテストファイルは _test.go とする。

テストの実行は go test で行う。

$ go test