
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
Keywords(キーワード)
以下のキーワードは予約されており、識別子として使用することはできません。
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
解説
キーワードとは何か?
キーワード(予約語) は、Go言語で特別な意味を持つ単語です。これらは言語の文法として組み込まれているため、変数名や関数名として使うことができません。
たとえ話: 日本語の「です」「ます」「が」「を」といった助詞や助動詞のように、文法上の役割が決まっている言葉です。これらを人の名前として使えないのと同じように、キーワードは識別子として使えません。
package main
func main() {
// ❌ エラー! キーワードは識別子として使えない
// var if int = 10
// var func string = "test"
// var return bool = true
// ✅ 正しい
var myIf int = 10
var myFunc string = "test"
var returnValue bool = true
}
Goの25個のキーワード
Goにはたった25個のキーワードしかありません(他の言語と比べて非常に少ない)。これはGoが「シンプルで覚えやすい」ことを重視しているからです。
参考:
- C++: 約90個
- Java: 約50個
- Python: 約35個
- Go: 25個
これらを用途別に分類して見ていきましょう。
1. プログラム構造に関するキーワード(5個)
package
パッケージ宣言に使います。すべてのGoファイルは必ずpackage宣言で始まります。
package main // 実行可能プログラムのエントリーポイント
package utils // ライブラリパッケージ
package myapp // カスタムパッケージ
用途: コードを論理的なまとまりに整理する
import
他のパッケージをインポートするときに使います。
package main
import "fmt" // 単一インポート
import (
"fmt" // 複数インポート
"os"
"net/http"
)
用途: 標準ライブラリや外部ライブラリの機能を使う
func
関数を定義するときに使います。
package main
import "fmt"
// 通常の関数
func greet(name string) {
fmt.Printf("こんにちは、%sさん\n", name)
}
// 戻り値がある関数
func add(a, b int) int {
return a + b
}
// 複数の戻り値
func getValues() (int, string) {
return 42, "太郎"
}
func main() {
greet("花子")
sum := add(10, 20)
fmt.Println(sum)
}
用途: 再利用可能なコードブロックを作成
type
新しい型を定義するときに使います。
package main
import "fmt"
// 構造体の定義
type User struct {
Name string
Age int
}
// 型エイリアス
type UserID int
// インターフェースの定義
type Writer interface {
Write(data []byte) (int, error)
}
func main() {
user := User{Name: "太郎", Age: 25}
var id UserID = 12345
fmt.Println(user, id)
}
用途: カスタム型、構造体、インターフェースの定義
struct
構造体を定義するときに使います(typeと組み合わせて使う)。
package main
import "fmt"
// 構造体の定義
type Person struct {
Name string
Age int
Address string
}
// 埋め込み構造体
type Employee struct {
Person // Personを埋め込む
EmployeeID int
Department string
}
func main() {
person := Person{
Name: "太郎",
Age: 30,
Address: "東京",
}
employee := Employee{
Person: Person{
Name: "花子",
Age: 28,
},
EmployeeID: 12345,
Department: "営業",
}
fmt.Println(person)
fmt.Println(employee)
}
用途: 複数のフィールドをまとめたデータ構造を作成
2. 変数・定数宣言に関するキーワード(2個)
var
変数を宣言するときに使います。
package main
import "fmt"
func main() {
// 基本的な宣言
var name string = "太郎"
// 型推論
var age = 25 // intと推論される
// ゼロ値で初期化
var count int // 0で初期化される
// 複数同時宣言
var x, y, z int = 1, 2, 3
// ブロックでまとめて宣言
var (
firstName string = "太郎"
lastName string = "山田"
userAge int = 30
)
fmt.Println(name, age, count, x, y, z)
fmt.Println(firstName, lastName, userAge)
}
短縮構文(:=)との違い:
package main
func main() {
// var を使った宣言
var name string = "太郎"
// 短縮構文(関数内のみ)
age := 25
// パッケージレベルではvarが必要
// packageLevel := 100 // エラー! パッケージレベルでは := 使えない
}
var packageLevel int = 100 // OK
const
定数を宣言するときに使います。定数は値を変更できません。
package main
import "fmt"
// 単一の定数
const Pi = 3.14159
// 複数の定数
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
// iotaを使った連番
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
)
func main() {
const localConst = "ローカル定数"
// エラー! 定数は変更できない
// Pi = 3.14 // コンパイルエラー
fmt.Println(Pi, StatusOK, Sunday, localConst)
}
用途: 変更されない値(設定値、ステータスコード、数学定数など)
3. 制御フロー(条件分岐)に関するキーワード(6個)
if、else
条件分岐を行います。
package main
import "fmt"
func main() {
age := 20
// 基本的なif文
if age >= 20 {
fmt.Println("成人です")
}
// if-else
if age >= 20 {
fmt.Println("成人です")
} else {
fmt.Println("未成年です")
}
// if-else if-else
score := 85
if score >= 90 {
fmt.Println("優秀")
} else if score >= 70 {
fmt.Println("良好")
} else {
fmt.Println("要努力")
}
// 短縮構文(変数宣言 + 条件)
if num := getNumber(); num > 0 {
fmt.Println("正の数:", num)
} else {
fmt.Println("0または負の数:", num)
}
}
func getNumber() int {
return 42
}
switch、case、default、fallthrough
複数の条件から選択します。
package main
import "fmt"
func main() {
// 基本的なswitch
day := "月曜日"
switch day {
case "月曜日":
fmt.Println("週の始まり")
case "金曜日":
fmt.Println("週末が近い")
case "土曜日", "日曜日": // 複数の値
fmt.Println("週末です")
default:
fmt.Println("平日です")
}
// 式を持たないswitch(if-else if-elseの代替)
score := 85
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
case score >= 70:
fmt.Println("C")
default:
fmt.Println("D")
}
// fallthroughの使用(次のcaseも実行)
num := 2
switch num {
case 1:
fmt.Println("1")
fallthrough // 次のcaseも実行
case 2:
fmt.Println("2")
fallthrough
case 3:
fmt.Println("3")
}
// 出力: 2, 3
}
注意: Goのswitchは、C言語と違って自動的にbreakされます。次のcaseも実行したい場合のみfallthroughを使います。
4. ループに関するキーワード(4個)
for
繰り返し処理を行います。Goにはforしかループがありません(whileやdo-whileはない)。
package main
import "fmt"
func main() {
// 基本的なfor(C言語スタイル)
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// while風のfor
count := 0
for count < 5 {
fmt.Println(count)
count++
}
// 無限ループ
// for {
// fmt.Println("無限ループ")
// break // breakで抜ける
// }
// rangeを使ったループ
numbers := []int{10, 20, 30}
for index, value := range numbers {
fmt.Printf("numbers[%d] = %d\n", index, value)
}
// インデックスだけ使う
for index := range numbers {
fmt.Println(index)
}
// 値だけ使う
for _, value := range numbers {
fmt.Println(value)
}
}
range
配列、スライス、マップ、チャネルを反復処理します。
package main
import "fmt"
func main() {
// スライスのrange
fruits := []string{"りんご", "バナナ", "みかん"}
for i, fruit := range fruits {
fmt.Printf("%d: %s\n", i, fruit)
}
// マップのrange
prices := map[string]int{
"りんご": 100,
"バナナ": 80,
}
for name, price := range prices {
fmt.Printf("%s: %d円\n", name, price)
}
// 文字列のrange(ルーン単位)
text := "こんにちは"
for i, char := range text {
fmt.Printf("%d: %c\n", i, char)
}
}
break
ループを抜けるときに使います。
package main
import "fmt"
func main() {
// 条件でループを抜ける
for i := 0; i < 10; i++ {
if i == 5 {
break // i==5でループ終了
}
fmt.Println(i)
}
// 出力: 0, 1, 2, 3, 4
// ラベル付きbreak(ネストループを抜ける)
OuterLoop:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break OuterLoop // 外側のループも抜ける
}
fmt.Printf("i=%d, j=%d\n", i, j)
}
}
}
continue
現在の反復をスキップして次の反復に進みます。
package main
import "fmt"
func main() {
// 偶数だけ出力(奇数をスキップ)
for i := 0; i < 10; i++ {
if i%2 != 0 {
continue // 奇数はスキップ
}
fmt.Println(i)
}
// 出力: 0, 2, 4, 6, 8
// ラベル付きcontinue
OuterLoop:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if j == 1 {
continue OuterLoop // 外側ループの次の反復へ
}
fmt.Printf("i=%d, j=%d\n", i, j)
}
}
}
5. 関数に関するキーワード(3個)
return
関数から値を返す、または関数を終了します。
package main
import "fmt"
// 値を返す
func add(a, b int) int {
return a + b
}
// 複数の値を返す
func divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("0で割れません")
}
return a / b, nil
}
// 名前付き戻り値
func calculate(a, b int) (sum, diff int) {
sum = a + b
diff = a - b
return // 名前付き戻り値は自動的に返される
}
// 値を返さない関数
func printMessage() {
fmt.Println("メッセージ")
return // 省略可能
}
func main() {
result := add(10, 20)
fmt.Println(result)
quotient, err := divide(10, 2)
if err != nil {
fmt.Println("エラー:", err)
} else {
fmt.Println(quotient)
}
s, d := calculate(10, 5)
fmt.Println(s, d)
}
defer
関数の終了時に実行される処理を登録します。
package main
import "fmt"
func main() {
// deferは関数終了時に実行される(LIFO: 後入れ先出し)
defer fmt.Println("3番目に実行")
defer fmt.Println("2番目に実行")
defer fmt.Println("1番目に実行")
fmt.Println("通常の処理")
// 出力:
// 通常の処理
// 1番目に実行
// 2番目に実行
// 3番目に実行
}
// 実用例: リソースのクリーンアップ
func readFile() {
file, err := os.Open("test.txt")
if err != nil {
return
}
defer file.Close() // 関数終了時に必ずファイルを閉じる
// ファイル読み込み処理
// ...
}
go
ゴルーチン(並行処理)を開始します。
package main
import (
"fmt"
"time"
)
func sayHello(name string) {
fmt.Printf("こんにちは、%sさん\n", name)
}
func main() {
// 通常の関数呼び出し
sayHello("太郎")
// ゴルーチンで並行実行
go sayHello("花子")
go sayHello("次郎")
// メインゴルーチンが終了しないように待つ
time.Sleep(1 * time.Second)
// 無名関数をゴルーチンで実行
go func() {
fmt.Println("無名関数から")
}()
time.Sleep(1 * time.Second)
}
6. 並行処理に関するキーワード(3個)
chan
チャネル(データをやり取りする通路)を定義します。
package main
import "fmt"
func main() {
// チャネルの作成
ch := make(chan int)
// ゴルーチンでデータを送信
go func() {
ch <- 42 // チャネルに送信
}()
// チャネルから受信
value := <-ch
fmt.Println(value) // 42
// バッファ付きチャネル
bufferedCh := make(chan string, 2)
bufferedCh <- "Hello"
bufferedCh <- "World"
fmt.Println(<-bufferedCh) // Hello
fmt.Println(<-bufferedCh) // World
}
select
複数のチャネル操作から1つを選択します。
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "from ch1"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "from ch2"
}()
// 最初に準備ができたチャネルから受信
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
case <-time.After(3 * time.Second):
fmt.Println("タイムアウト")
}
}
7. その他のキーワード(2個)
goto
指定したラベルにジャンプします(使用は非推奨)。
package main
import "fmt"
func main() {
i := 0
Loop:
fmt.Println(i)
i++
if i < 5 {
goto Loop // Loopラベルにジャンプ
}
fmt.Println("終了")
}
注意: gotoは可読性を損なうため、できるだけ使わないことが推奨されます。
interface
インターフェース(メソッドの集合)を定義します。
package main
import "fmt"
// インターフェースの定義
type Shape interface {
Area() float64
Perimeter() float64
}
// 構造体
type Rectangle struct {
Width float64
Height float64
}
// Rectangleがshapeインターフェースを実装
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
func main() {
var s Shape = Rectangle{Width: 10, Height: 5}
fmt.Println("面積:", s.Area())
fmt.Println("周囲:", s.Perimeter())
}
8. その他の型関連キーワード(1個)
map
マップ(連想配列)を定義します。
package main
import "fmt"
func main() {
// マップの作成
prices := make(map[string]int)
prices["りんご"] = 100
prices["バナナ"] = 80
// リテラルでの初期化
ages := map[string]int{
"太郎": 25,
"花子": 30,
}
// 値の取得
price := prices["りんご"]
fmt.Println(price) // 100
// 存在チェック
age, exists := ages["太郎"]
if exists {
fmt.Println("年齢:", age)
}
// 削除
delete(prices, "りんご")
// イテレーション
for name, age := range ages {
fmt.Printf("%s: %d歳\n", name, age)
}
}
まとめ: キーワードで覚えておくべきこと
キーワードの分類表
| 分類 | キーワード |
|---|---|
| プログラム構造 | package, import, func, type, struct |
| 変数・定数 | var, const |
| 条件分岐 | if, else, switch, case, default, fallthrough |
| ループ | for, range, break, continue |
| 関数 | return, defer, go |
| 並行処理 | chan, select |
| その他 | goto, interface, map |
重要なポイント
- キーワードは識別子として使えない
- Goには25個のキーワードしかない(シンプル!)
- 大文字・小文字を変えても使えない(
Ifもエラー) - 最もよく使うのは:
package,import,func,var,if,for,return
実用的なアドバイス
package main // package
import "fmt" // import
type User struct { // type, struct
Name string
}
func main() { // func
var count int = 0 // var
const Max = 100 // const
for i := 0; i < 10; i++ { // for
if i%2 == 0 { // if
count++
} else { // else
continue // continue
}
}
result := make(map[string]int) // map
ch := make(chan int) // chan
defer fmt.Println("終了") // defer
go func() { // go
ch <- 42
}()
select { // select
case val := <-ch:
fmt.Println(val)
}
return // return
}
キーワードを理解することは、Goの文法の基礎を理解することです。25個しかないので、プログラミングしながら自然に覚えられます!
おわりに
本日は、Go言語の言語仕様について解説しました。

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)

コメント