Go言語入門:言語仕様 -Vol.12-

スポンサーリンク
Go言語入門:言語仕様 -Vol.12- ノウハウ
Go言語入門:言語仕様 -Vol.12-
この記事は約19分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

本日は、Go言語の言語仕様について解説しています。

スポンサーリンク

背景

Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。

言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。

そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。

言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!

Integer literals(整数リテラル)

整数リテラルは、整数定数を表す数字の列です。オプションのプレフィックスで非10進数の基数を設定します。0bまたは0Bは2進数、00o、または0Oは8進数、0xまたは0Xは16進数を表します[Go 1.13]。単一の0は10進数のゼロと見なされます。16進数リテラルでは、文字aからfおよびAからFは値10から15を表します。

可読性のために、アンダースコア文字_を基数プレフィックスの後、または連続する数字の間に配置できます。このようなアンダースコアはリテラルの値を変更しません。

int_lit        = decimal_lit | binary_lit | octal_lit | hex_lit .
decimal_lit    = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
binary_lit     = "0" ( "b" | "B" ) [ "_" ] binary_digits .
octal_lit      = "0" [ "o" | "O" ] [ "_" ] octal_digits .
hex_lit        = "0" ( "x" | "X" ) [ "_" ] hex_digits .

decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
binary_digits  = binary_digit { [ "_" ] binary_digit } .
octal_digits   = octal_digit { [ "_" ] octal_digit } .
hex_digits     = hex_digit { [ "_" ] hex_digit } .
42
4_2
0600
0_600
0o600
0O600       // 2文字目は大文字の'O'
0xBadFace
0xBad_Face
0x_67_7a_2f_cc_40_c6
170141183460469231731687303715884105727
170_141183_460469_231731_687303_715884_105727

_42         // 識別子であり、整数リテラルではない
42_         // 無効: _ は連続する数字を区切る必要がある
4__2        // 無効: _ は一度に1つのみ
0_xBadFace  // 無効: _ は連続する数字を区切る必要がある

解説

整数リテラルとは何か?

整数リテラルは、プログラム内に直接書かれた整数の値です。「42」「100」「0xFF」などがこれに当たります。

たとえ話: 料理のレシピに「砂糖50g」と書かれているとき、「50」が数値リテラルです。変数名ではなく、値そのものを直接書いています。

package main

import "fmt"

func main() {
    // これらはすべて整数リテラル
    age := 25        // 10進数
    maxUsers := 1000 // 10進数
    permissions := 0755  // 8進数
    color := 0xFF00FF    // 16進数
    
    fmt.Println(age, maxUsers, permissions, color)
}

1. 10進数リテラル(Decimal literals)

最も一般的な数値表記です。0以外は0で始められません

基本的な10進数

package main

import "fmt"

func main() {
    // 10進数リテラル
    var zero int = 0      // 単一の0
    var one int = 1
    var ten int = 10
    var hundred int = 100
    var million int = 1000000
    
    fmt.Println(zero, one, ten, hundred, million)
    
    // 注意: 0で始まる数は8進数になる(後述)
    // var x int = 0123  // これは8進数の123(10進数の83)
}

アンダースコアで見やすく[Go 1.13]

大きな数値を読みやすくするために、**アンダースコア_**で桁を区切れます。

package main

import "fmt"

func main() {
    // アンダースコアなし
    population := 1000000  // 100万? 1000万?
    
    // アンダースコアで3桁区切り(読みやすい!)
    population2 := 1_000_000  // 明らかに100万
    
    // 日本式の4桁区切りも可能
    yen := 1_0000_0000  // 1億円
    
    // 様々な区切り方
    creditCard := 1234_5678_9012_3456  // クレジットカード風
    
    fmt.Println(population2, yen, creditCard)
    
    // アンダースコアは値に影響しない
    fmt.Println(1000000 == 1_000_000)  // true
}

実用例:

package main

const (
    KB = 1_024           // 1キロバイト
    MB = 1_024 * KB      // 1メガバイト
    GB = 1_024 * MB      // 1ギガバイト
    
    Million = 1_000_000           // 100万
    Billion = 1_000_000_000       // 10億
    
    MaxInt64 = 9_223_372_036_854_775_807  // int64の最大値
)

2. 2進数リテラル(Binary literals)

0bまたは0Bで始まり、01だけを使います。

package main

import "fmt"

func main() {
    // 2進数リテラル
    var b1 int = 0b1010  // 10進数の10
    var b2 int = 0B1111  // 10進数の15
    
    fmt.Println(b1, b2)
    
    // ビットパターンが見やすい
    var flags int = 0b00001111  // 下位4ビットが1
    
    // アンダースコアで8ビット(1バイト)ごとに区切る
    var ipAddress uint32 = 0b11000000_10101000_00000001_00000001
    // 192.168.1.1のビット表現
    
    fmt.Printf("フラグ: %b\n", flags)
    fmt.Printf("IPアドレス: %d.%d.%d.%d\n",
        (ipAddress>>24)&0xFF,
        (ipAddress>>16)&0xFF,
        (ipAddress>>8)&0xFF,
        ipAddress&0xFF)
}

使用場面:

package main

import "fmt"

// ビットフラグの定義
const (
    FlagRead    = 0b0001  // 読み取り
    FlagWrite   = 0b0010  // 書き込み
    FlagExecute = 0b0100  // 実行
    FlagDelete  = 0b1000  // 削除
)

func main() {
    // 権限の組み合わせ
    permissions := FlagRead | FlagWrite  // 0b0011
    
    // 読み取り権限があるかチェック
    if permissions&FlagRead != 0 {
        fmt.Println("読み取り可能")
    }
    
    // 実行権限がないことを確認
    if permissions&FlagExecute == 0 {
        fmt.Println("実行不可")
    }
}

3. 8進数リテラル(Octal literals)

従来の書き方: 0で始まる

package main

import "fmt"

func main() {
    // 0で始まると8進数
    var octal1 int = 0755  // 8進数の755 = 10進数の493
    var octal2 int = 0644  // 8進数の644 = 10進数の420
    
    fmt.Println(octal1, octal2)
    
    // 注意: 0で始まるつもりがなくても...
    var wrong int = 0123  // これは10進数の123ではなく、8進数の123!
    fmt.Println(wrong)    // 83 が出力される
}

新しい書き方: 0oまたは0Oで始まる[Go 1.13]

package main

import "fmt"

func main() {
    // 明示的に8進数であることを示す(推奨)
    var octal1 int = 0o755  // 8進数の755
    var octal2 int = 0O644  // 大文字Oでも可
    
    fmt.Println(octal1, octal2)
    
    // アンダースコアも使える
    var octal3 int = 0o7_5_5
    fmt.Println(octal3)
}

主な使用場面: Unixファイルパーミッション

package main

import (
    "fmt"
    "os"
)

func main() {
    // ファイルパーミッション(8進数で表現)
    // 0o644 = rw-r--r--
    // 所有者: 読み書き可(6 = 4+2 = r+w)
    // グループ: 読み取りのみ(4 = r)
    // その他: 読み取りのみ(4 = r)
    
    file, err := os.OpenFile(
        "test.txt",
        os.O_CREATE|os.O_WRONLY,
        0o644,  // パーミッション
    )
    if err != nil {
        fmt.Println("エラー:", err)
        return
    }
    defer file.Close()
    
    // よく使われるパーミッション
    const (
        PermReadWrite     = 0o644  // rw-r--r--
        PermReadWriteExec = 0o755  // rwxr-xr-x
        PermReadOnly      = 0o444  // r--r--r--
        PermPrivate       = 0o600  // rw-------
    )
}

8進数の各桁の意味:

数値2進数権限説明
0000なし
1001–x実行のみ
2010-w-書き込みのみ
3011-wx書き込み+実行
4100r–読み取りのみ
5101r-x読み取り+実行
6110rw-読み取り+書き込み
7111rwxすべて

4. 16進数リテラル(Hexadecimal literals)

0xまたは0Xで始まり、0-9a-f(またはA-F)を使います。

package main

import "fmt"

func main() {
    // 16進数リテラル
    var hex1 int = 0xFF      // 10進数の255
    var hex2 int = 0x10      // 10進数の16
    var hex3 int = 0xBadFace // 大文字小文字混在OK
    
    fmt.Println(hex1, hex2, hex3)
    
    // 小文字でも可
    var hex4 int = 0xff
    var hex5 int = 0xbadface
    
    fmt.Println(hex4, hex5)
    
    // アンダースコアで見やすく
    var hex6 int = 0xBad_Face
    var hex7 int = 0x_67_7a_2f_cc_40_c6  // 2桁ごとに区切る
    
    fmt.Println(hex6, hex7)
}

使用場面1: 色の表現

package main

import "fmt"

func main() {
    // RGBカラー(16進数で表現)
    const (
        ColorRed     = 0xFF0000  // R:255, G:0,   B:0
        ColorGreen   = 0x00FF00  // R:0,   G:255, B:0
        ColorBlue    = 0x0000FF  // R:0,   G:0,   B:255
        ColorWhite   = 0xFFFFFF  // R:255, G:255, B:255
        ColorBlack   = 0x000000  // R:0,   G:0,   B:0
        ColorGray    = 0x808080  // R:128, G:128, B:128
        ColorPink    = 0xFF69B4  // ピンク
        ColorOrange  = 0xFFA500  // オレンジ
    )
    
    // RGBに分解
    color := ColorPink
    r := (color >> 16) & 0xFF  // 255
    g := (color >> 8) & 0xFF   // 105
    b := color & 0xFF          // 180
    
    fmt.Printf("RGB: (%d, %d, %d)\n", r, g, b)
    fmt.Printf("カラーコード: #%06X\n", color)
}

使用場面2: バイトデータ

package main

import "fmt"

func main() {
    // バイト列を16進数で表現
    data := []byte{
        0x48, 0x65, 0x6C, 0x6C, 0x6F,  // "Hello"のASCIIコード
    }
    
    fmt.Printf("16進数: % X\n", data)  // 48 65 6C 6C 6F
    fmt.Printf("文字列: %s\n", data)    // Hello
    
    // マジックナンバー(ファイル形式の識別)
    const (
        MagicPNG  = 0x89504E47  // PNGファイルの先頭
        MagicJPEG = 0xFFD8FFE0  // JPEGファイルの先頭
        MagicGIF  = 0x47494638  // GIFファイルの先頭
    )
}

使用場面3: メモリアドレス

package main

import "fmt"

func main() {
    x := 42
    ptr := &x
    
    // ポインタ(メモリアドレス)は16進数で表示される
    fmt.Printf("アドレス: %p\n", ptr)      // 0xc0000140a0 など
    fmt.Printf("アドレス: 0x%x\n", ptr)    // 同じ
    
    // 特定のメモリアドレスを表現(低レベルプログラミング)
    const (
        BaseAddress   = 0x40000000
        OffsetAddress = 0x00001000
    )
    
    targetAddr := BaseAddress + OffsetAddress
    fmt.Printf("ターゲットアドレス: 0x%X\n", targetAddr)
}

5. アンダースコアのルール

正しい使い方

package main

import "fmt"

func main() {
    // ✅ 正しい使い方
    
    // 基数プレフィックスの後
    var a int = 0x_FFFF
    var b int = 0b_1010
    var c int = 0o_755
    
    // 数字と数字の間
    var d int = 1_000_000
    var e int = 0xFF_FF_FF
    
    // 複数のアンダースコア(別々の場所)
    var f int = 1_234_567_890
    
    fmt.Println(a, b, c, d, e, f)
}

間違った使い方

package main

func main() {
    // ❌ 間違った使い方(コンパイルエラー)
    
    // 識別子として解釈される
    // var x int = _42  // これは変数名 "_42"
    
    // 末尾にアンダースコア
    // var y int = 42_  // エラー!
    
    // 連続するアンダースコア
    // var z int = 4__2  // エラー!
    
    // プレフィックスと数字の間(xの後)
    // var w int = 0_xBadFace  // エラー!
    
    // 正しくは:
    var correct1 int = 0x_BadFace  // OK
    var correct2 int = 0xBad_Face  // OK
}

6. 非常に大きな整数

Goの整数リテラルは任意の大きさを持てます(ただし、変数に代入するときは型の範囲内に収まる必要があります)。

package main

import (
    "fmt"
    "math/big"
)

func main() {
    // 巨大な整数リテラル
    huge := 170141183460469231731687303715884105727
    fmt.Println(huge)
    
    // アンダースコアで見やすく
    huge2 := 170_141183_460469_231731_687303_715884_105727
    fmt.Println(huge2)
    
    // 注意: int64の範囲を超える場合はbig.Intを使う
    bigNum := new(big.Int)
    bigNum.SetString("170141183460469231731687303715884105727", 10)
    fmt.Println(bigNum)
    
    // 計算もできる
    bigNum2 := new(big.Int)
    bigNum2.SetString("123456789012345678901234567890", 10)
    
    result := new(big.Int)
    result.Mul(bigNum, bigNum2)
    fmt.Println(result)
}

7. 実践的な使用例

例1: 設定値を読みやすく

package main

const (
    // タイムアウト(ミリ秒)
    TimeoutShort  = 1_000    // 1秒
    TimeoutMedium = 30_000   // 30秒
    TimeoutLong   = 300_000  // 5分
    
    // データサイズ
    MaxFileSize   = 100_000_000  // 100MB
    MaxUploadSize = 10_000_000   // 10MB
    
    // 制限値
    MaxUsers      = 10_000
    MaxRequests   = 1_000_000
)

例2: ビットマスクとフラグ

package main

import "fmt"

const (
    // ファイルオープンモード(8進数)
    O_RDONLY = 0o0      // 読み取り専用
    O_WRONLY = 0o1      // 書き込み専用
    O_RDWR   = 0o2      // 読み書き
    O_CREATE = 0o100    // 作成
    O_TRUNC  = 0o1000   // 切り詰め
    
    // 色定義(16進数)
    ColorTransparent = 0x00000000
    ColorSemiTrans   = 0x80FFFFFF
    ColorOpaque      = 0xFFFFFFFF
    
    // ビットフラグ(2進数)
    FlagA = 0b0001
    FlagB = 0b0010
    FlagC = 0b0100
    FlagD = 0b1000
)

func main() {
    // 複数フラグの組み合わせ
    mode := O_RDWR | O_CREATE
    fmt.Printf("モード: 0o%o\n", mode)
    
    flags := FlagA | FlagC
    fmt.Printf("フラグ: 0b%04b\n", flags)
}

例3: プロトコルの定義

package main

import "fmt"

// HTTPステータスコード
const (
    StatusOK                   = 200
    StatusCreated              = 201
    StatusBadRequest           = 400
    StatusUnauthorized         = 401
    StatusForbidden            = 403
    StatusNotFound             = 404
    StatusInternalServerError  = 500
)

// IPアドレス(16進数で32ビット表現)
const (
    IPLocalhost = 0x7F000001  // 127.0.0.1
    IPBroadcast = 0xFFFFFFFF  // 255.255.255.255
)

func main() {
    fmt.Println("ステータス:", StatusOK)
    
    ip := IPLocalhost
    fmt.Printf("IPアドレス: %d.%d.%d.%d\n",
        (ip>>24)&0xFF,
        (ip>>16)&0xFF,
        (ip>>8)&0xFF,
        ip&0xFF)
}

まとめ: 整数リテラルで覚えておくべきこと

基数の書き方

基数プレフィックス使える数字
10進数なし0-942, 1_000_000
2進数0b, 0B0-10b1010, 0b_1111_0000
8進数0, 0o, 0O0-70o755, 0644
16進数0x, 0X0-9, a-f, A-F0xFF, 0xBad_Face

アンダースコアのルール

  • 数字と数字の間: 1_000, 0xFF_FF
  • プレフィックスの後: 0x_FF, 0b_1010
  • 先頭や末尾: _42, 42_
  • 連続: 4__2
  • プレフィックス内: 0_x42

実用的なアドバイス

package main

const (
    // 10進数: 日常的な数値
    UserLimit = 1_000_000
    
    // 2進数: ビット操作
    Permission = 0b0111
    
    // 8進数: ファイルパーミッション
    FileMode = 0o644
    
    // 16進数: 色、バイトデータ
    Color = 0xFF5733
)

func main() {
    // 適切な基数を選ぶことで、
    // コードの意図が明確になる
}

整数リテラルは、コードを読みやすくするための重要な要素です。用途に応じて適切な基数とアンダースコアを使いましょう!

おわりに 

本日は、Go言語の言語仕様について解説しました。

よっしー
よっしー

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

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました