Go言語の特徴
この章では、他のプログラミング言語と比較したときのGoの特徴について学びます。これにより、シンプルな仕様・単一バイナリ配布・goroutineによる並行処理など、Goが選ばれる理由が理解できます。
1. 本章の概要
1.1 本章の目的
Goは、DevOps系ツールやマイクロサービスの実装言語として選ばれる場面が多い言語です。他言語との違いを最初に押さえておくと、以降のハンズオンで「なぜGoではこう書くのか」の背景がクリアになり、他言語の書き方に引きずられずに済みます。
1.2 本章で学ぶ内容
シンプルな言語仕様、静的型付けと型推論、単一バイナリでの配布、goroutineによる並行処理、標準ライブラリの豊富さといったGoの主要な特徴を扱います。あわせて、Goが実務でよく使われる代表的な領域(CLIツール・APIサーバ・クラウドネイティブ基盤)も整理します。
2. 仕様が小さくシンプル
2.1 Goがシンプルな仕様を選んでいる理由
多くの言語は「言語仕様を豊かにすることで、あらゆる書き方に対応する」方向に発展してきましたが、Go はあえて逆の方向を選んでいます。キーワードは25個だけ、繰り返し構文は for の1つだけ、例外機構はなし、と極端なほど機能を絞っています。この設計判断により、Go には以下のメリットがあります。
- 学習コストが低く、他人が書いたコードを短時間で読み解ける
- 同じ処理の「書き方の流派」が生まれにくく、チーム全員のコードスタイルが揃いやすい
- 言語のバージョンアップで大きな破壊的変更が入りにくい
2.2 forによる繰り返しの書き方
たとえば繰り返し構文は for の1つだけで、他言語の while や do-while に相当する形もすべて for の書き方の変化で表現します。
// 従来の for
for i := 0; i < 5; i++ { }
// while のように使う
for cond { }
// 無限ループ
for { }
例外機構がない代わりに、エラーは戻り値として扱います。
result, err := doSomething()
if err != nil {
return err
}
「例外を投げるかもしれない関数」と「投げない関数」を見分けるためにドキュメントを読む必要がなく、シグネチャを見れば失敗する可能性が明示されているのが Go の特徴です。詳しい書き方は「Go制御構文」や「Go関数と型定義」で扱います。
| 💡 ポイント |
|---|
| 「機能が少ない」ことは制約ではなく、Goでは書き方の選択肢を減らして意思決定コストを下げるという設計思想として意図的に選ばれています。慣れると、この制約こそが「読みやすい大規模コード」を支えていることが実感できます。 |
3. コンパイル速度と単一バイナリでの配布
3.1 コンパイル型でありながら手軽な理由
Go はコンパイル型言語で、事前に機械語に変換してから実行します。C や Java もコンパイル型ですが、Go には以下の運用上のメリットがあります。
- コンパイルが極めて速い。中規模プロジェクトでも数秒でビルドが完了する
- 生成されるのはランタイムを内包した単一バイナリ。Go 本体をインストールしていない環境でもそのまま実行できる
- クロスコンパイル(他OS向けのビルド)が 1コマンドでできる
Python のようなインタプリタ型言語では、本番サーバに Python 本体・仮想環境・依存パッケージを揃える必要があります。Java の JVM 系言語では、実行環境に JRE などが必要です。Go はこの手順が不要で、ビルドしたバイナリを SCP や S3 経由で1ファイル送るだけでデプロイが完了します。
3.2 go buildとクロスコンパイルの実行例
main.go を書いたあと、以下のように go build を実行するだけで単一バイナリが生成されます。
go build -o myapp main.go
生成された myapp はそのまま実行できます。
./myapp
他のOS向けのバイナリも、環境変数を切り替えるだけで作れます。
GOOS=linux GOARCH=amd64 go build -o myapp-linux main.go
このコマンドは Mac 上で実行しても、Linux(amd64)用のバイナリを出力します。CI で Linux 用ビルドを回したり、Windows と Mac の両方に配布する CLI ツールを1リポジトリで管理する、といった運用がとても楽になります。
具体的な使い方は「Goモジュールと標準ライブラリ」や「GoでCLIツールを作ろう」で扱います。
| 💡 ポイント |
|---|
| コンパイル速度は開発体験に直結します。ビルドが数秒で終わるため、Go では「コードを書く → 少しビルドして動作確認 → 続きを書く」のサイクルを、インタプリタ型に近いテンポで回せます。大規模プロジェクトでも待ち時間で集中が切れにくく、この点も Go が選ばれる大きな理由の一つです。 |
4. goroutine による並行処理
4.1 Goにgoroutineが組み込まれている理由
サーバやCLIツールでは、「複数のリクエストを同時に捌く」「バックグラウンドで別の処理を進めながら本処理を続ける」といった並行処理が頻繁に必要になります。他言語ではスレッドやコルーチン、async/await などを使いますが、いずれも扱いは重く、正しく書くのに気を遣います。
Go は言語標準機能として goroutine(軽量スレッド)を持ちます。特徴を並べると以下です。
goキーワードを関数呼び出しの前に付けるだけで並行実行できる- 1つの goroutine のメモリは数KB程度と非常に軽く、数万〜数十万の同時実行にも耐える
- channel を使えば goroutine 間で値を安全に受け渡せる
4.2 goroutineとchannelの実装例
たとえば「複数の外部URLを並行に叩いて、レスポンスを集める」処理は、Go だと直感的に書けます。
func fetch(url string, ch chan string) {
resp, _ := http.Get(url)
defer resp.Body.Close()
ch <- fmt.Sprintf("%s: %d", url, resp.StatusCode)
}
func main() {
urls := []string{"https://example.com", "https://example.org"}
ch := make(chan string, len(urls))
for _, u := range urls {
go fetch(u, ch)
}
for range urls {
fmt.Println(<-ch)
}
}
go fetch(u, ch) の一行で、URLごとに新しい goroutine が立ち上がり、並行に HTTP リクエストが実行されます。取得結果は channel(ch)を通してメインの goroutine に渡され、順に表示されます。
詳しくは「Goの並行処理」で扱います。
| 💡 ポイント |
|---|
| Go は「メモリを共有して通信するのではなく、通信によってメモリを共有する」という設計哲学("Do not communicate by sharing memory; instead, share memory by communicating.")を持っています。channel を中心にしたこの発想が、複雑になりがちな並行処理を読みやすいコードに保つ支柱になっています。 |
5. まとめ
この章では、Go言語の特徴について学びました。
- Goは、仕様が小さくシンプルな言語で、キーワード数や書き方の選択肢を意図的に絞ることで「読みやすい大規模コード」を支える設計になっている
- コンパイル速度が速く、生成されるバイナリはランタイムを内包した単一実行ファイルで、クロスコンパイルも
GOOS/GOARCHの指定だけで完結する - goroutineとchannelにより、並行処理を軽く扱える(数KB程度のメモリで動くgoroutineを数万単位で走らせても破綻しない)
次の章では、Goの基本文法を学びつつ、実際に変数・基本型・型推論を体験します。