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

スポンサーリンク
Go言語入門:言語仕様 -Vol.16- 用語解説
Go言語入門:言語仕様 -Vol.16-
この記事は約21分で読めます。
よっしー
よっしー

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

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

スポンサーリンク

背景

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

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

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

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

Constants(定数)

真偽値定数、ルーン定数、整数定数、浮動小数点定数、複素数定数、文字列定数があります。ルーン、整数、浮動小数点、複素数定数は総称して数値定数と呼ばれます。

定数値は、ルーン、整数、浮動小数点、虚数、または文字列リテラル、定数を示す識別子、定数式、結果が定数である変換、または定数引数に適用されるminmaxなどの一部の組み込み関数の結果値、特定の値に適用されるunsafe.Sizeof、一部の式に適用されるcapまたはlen、複素定数に適用されるrealおよびimag、数値定数に適用されるcomplexによって表されます。真偽値の真の値は、事前宣言された定数truefalseによって表されます。事前宣言された識別子iotaは整数定数を示します。

一般的に、複素定数は定数式の一形式であり、そのセクションで説明されます。

数値定数は、任意の精度の正確な値を表し、オーバーフローしません。したがって、IEEE 754の負のゼロ、無限大、非数値を示す定数はありません。

定数は型付きまたは型なしの場合があります。リテラル定数、truefalseiota、および型なし定数オペランドのみを含む特定の定数式は型なしです。

定数は、定数宣言または変換によって明示的に型を与えられるか、変数宣言または代入文で使用されるとき、または式のオペランドとして使用されるときに暗黙的に型を与えられます。定数値がそれぞれの型の値として表現できない場合はエラーです。型が型パラメータの場合、定数は型パラメータの非定数値に変換されます。

型なし定数には、型付きの値が必要なコンテキストで定数が暗黙的に変換される型であるデフォルト型があります。たとえば、明示的な型がないi := 0のような短縮変数宣言などです。型なし定数のデフォルト型は、それが真偽値、ルーン、整数、浮動小数点、複素数、または文字列定数であるかに応じて、それぞれboolruneintfloat64complex128、またはstringです。

実装上の制限: 言語では数値定数は任意の精度を持ちますが、コンパイラは限られた精度の内部表現を使用してそれらを実装する場合があります。とはいえ、すべての実装は次のことを行う必要があります。

  • 整数定数を少なくとも256ビットで表現する。
  • 浮動小数点定数(複素定数の部分を含む)を、少なくとも256ビットの仮数と少なくとも16ビットの符号付き2進指数で表現する。
  • 整数定数を正確に表現できない場合はエラーを出す。
  • オーバーフローのために浮動小数点または複素定数を表現できない場合はエラーを出す。
  • 精度の制限のために浮動小数点または複素定数を表現できない場合は、最も近い表現可能な定数に丸める。

これらの要件は、リテラル定数と定数式の評価結果の両方に適用されます。


解説

定数とは何か?

定数は、プログラムの実行中に値が変わらないデータです。変数とは対照的に、一度定義したら変更できません。

たとえ話: 数学の円周率π(3.14159…)のように、プログラム内で「変わらない値」を定義するのが定数です。設定値や固定的な計算式などに使われます。

package main

import "fmt"

func main() {
    // 定数の宣言(値は変更できない)
    const Pi = 3.14159
    const AppName = "MyApp"
    const MaxUsers = 100
    
    fmt.Println(Pi, AppName, MaxUsers)
    
    // エラー! 定数は変更できない
    // Pi = 3.14  // cannot assign to Pi
}

1. 定数の種類

Goには6種類の定数があります。

真偽値定数(Boolean constants)

package main

import "fmt"

func main() {
    const IsEnabled = true
    const IsDebug = false
    
    fmt.Println(IsEnabled, IsDebug)
}

ルーン定数(Rune constants)

package main

import "fmt"

func main() {
    const CharA = 'A'
    const CharHiragana = 'あ'
    const CharEmoji = '😀'
    
    fmt.Printf("%c %c %c\n", CharA, CharHiragana, CharEmoji)
}

整数定数(Integer constants)

package main

import "fmt"

func main() {
    const Count = 100
    const MaxRetries = 5
    const HexValue = 0xFF
    const BinaryValue = 0b1010
    
    fmt.Println(Count, MaxRetries, HexValue, BinaryValue)
}

浮動小数点定数(Floating-point constants)

package main

import "fmt"

func main() {
    const Pi = 3.14159
    const E = 2.71828
    const Planck = 6.62607015e-34  // プランク定数
    
    fmt.Println(Pi, E, Planck)
}

複素数定数(Complex constants)

package main

import "fmt"

func main() {
    const C1 = 3 + 4i
    const C2 = 1.5 + 2.5i
    
    fmt.Println(C1, C2)
}

文字列定数(String constants)

package main

import "fmt"

func main() {
    const Greeting = "こんにちは"
    const Version = "1.0.0"
    const APIEndpoint = "https://api.example.com"
    
    fmt.Println(Greeting, Version, APIEndpoint)
}

2. 定数の宣言方法

単一の定数宣言

package main

const Pi = 3.14159
const AppName = "MyApp"

複数の定数を一度に宣言

package main

const (
    StatusOK       = 200
    StatusNotFound = 404
    StatusError    = 500
)

型付き定数

明示的に型を指定できます。

package main

import "fmt"

func main() {
    // 型なし定数(デフォルト)
    const a = 42
    
    // 型付き定数
    const b int = 42
    const c float64 = 3.14
    const d string = "hello"
    
    fmt.Printf("a: %T, b: %T, c: %T, d: %T\n", a, b, c, d)
    // a: int, b: int, c: float64, d: string
}

3. 型なし定数(Untyped constants)

型なし定数は、使われる文脈に応じて自動的に型が決まります。これがGoの定数の強力な特徴です。

デフォルト型

型なし定数にはデフォルト型があります:

定数の種類デフォルト型
真偽値bool
ルーンrune (= int32)
整数int
浮動小数点float64
複素数complex128
文字列string
package main

import "fmt"

func main() {
    // 型なし定数
    const a = 42        // デフォルト: int
    const b = 3.14      // デフォルト: float64
    const c = "hello"   // デフォルト: string
    const d = true      // デフォルト: bool
    const e = 'A'       // デフォルト: rune
    const f = 1 + 2i    // デフォルト: complex128
    
    // 変数に代入すると型が決まる
    var x = a  // int
    var y = b  // float64
    var z = c  // string
    
    fmt.Printf("%T %T %T\n", x, y, z)
}

柔軟な型変換

型なし定数は、様々な型に自動的に変換されます。

package main

import "fmt"

func main() {
    const x = 42  // 型なし整数定数
    
    // 様々な型の変数に代入できる
    var a int = x
    var b int64 = x
    var c float64 = x
    var d complex128 = x
    
    fmt.Println(a, b, c, d)  // 42 42 42 (42+0i)
    
    // 型付き定数だとこうはいかない
    const y int = 42
    // var e float64 = y  // エラー! 明示的な変換が必要
    var e float64 = float64(y)  // OK
}

4. 定数式(Constant expressions)

定数は、コンパイル時に評価できる式であれば、計算結果も定数になります。

算術演算

package main

import "fmt"

func main() {
    const (
        a = 10
        b = 20
        sum = a + b        // 30
        diff = a - b       // -10
        product = a * b    // 200
        quotient = b / a   // 2
    )
    
    fmt.Println(sum, diff, product, quotient)
}

複雑な式

package main

import "fmt"

func main() {
    const (
        Pi = 3.14159
        Radius = 5.0
        
        // 円の面積
        Area = Pi * Radius * Radius
        
        // 円の円周
        Circumference = 2 * Pi * Radius
    )
    
    fmt.Printf("面積: %.2f, 円周: %.2f\n", Area, Circumference)
}

5. 組み込み関数と定数

一部の組み込み関数は、定数引数に適用すると定数を返します。

len()

package main

import "fmt"

func main() {
    const str = "Hello"
    const length = len(str)  // 5 (定数)
    
    const arr = [3]int{1, 2, 3}
    const size = len(arr)  // 3 (定数)
    
    fmt.Println(length, size)
}

cap()

package main

import "fmt"

func main() {
    const arr = [5]int{1, 2, 3, 4, 5}
    const capacity = cap(arr)  // 5 (定数)
    
    fmt.Println(capacity)
}

min(), max()

package main

import "fmt"

func main() {
    const a = 10
    const b = 20
    const c = 5
    
    const minimum = min(a, b, c)  // 5
    const maximum = max(a, b, c)  // 20
    
    fmt.Println(minimum, maximum)
}

real(), imag(), complex()

package main

import "fmt"

func main() {
    const c = 3 + 4i
    
    const realPart = real(c)  // 3
    const imagPart = imag(c)  // 4
    
    const c2 = complex(realPart, imagPart)  // 3+4i
    
    fmt.Println(realPart, imagPart, c2)
}

6. iota – 連続した整数の生成

iotaは、定数宣言内で自動的に増加する整数を生成します。

基本的な使い方

package main

import "fmt"

func main() {
    const (
        Sunday = iota  // 0
        Monday         // 1
        Tuesday        // 2
        Wednesday      // 3
        Thursday       // 4
        Friday         // 5
        Saturday       // 6
    )
    
    fmt.Println(Sunday, Monday, Tuesday)  // 0 1 2
}

値をスキップ

package main

import "fmt"

func main() {
    const (
        _ = iota  // 0をスキップ
        KB = 1 << (10 * iota)  // 1 << 10 = 1024
        MB                      // 1 << 20 = 1048576
        GB                      // 1 << 30 = 1073741824
        TB                      // 1 << 40
    )
    
    fmt.Println(KB, MB, GB)
}

ビットフラグ

package main

import "fmt"

func main() {
    const (
        FlagRead = 1 << iota  // 1 << 0 = 1
        FlagWrite             // 1 << 1 = 2
        FlagExecute           // 1 << 2 = 4
        FlagDelete            // 1 << 3 = 8
    )
    
    fmt.Printf("Read:%d, Write:%d, Execute:%d, Delete:%d\n",
        FlagRead, FlagWrite, FlagExecute, FlagDelete)
    
    // 権限の組み合わせ
    permissions := FlagRead | FlagWrite  // 3
    fmt.Println("Permissions:", permissions)
}

複雑な式

package main

import "fmt"

func main() {
    const (
        a = iota * 10      // 0 * 10 = 0
        b                  // 1 * 10 = 10
        c                  // 2 * 10 = 20
    )
    
    const (
        x = iota + 1  // 0 + 1 = 1
        y             // 1 + 1 = 2
        z             // 2 + 1 = 3
    )
    
    fmt.Println(a, b, c)  // 0 10 20
    fmt.Println(x, y, z)  // 1 2 3
}

7. 任意精度(Arbitrary precision)

Goの定数は、任意の精度を持ちます。これは非常に大きな数や非常に高精度な数を扱えることを意味します。

巨大な整数

package main

import "fmt"

func main() {
    // 非常に大きな整数定数
    const BigNumber = 12345678901234567890123456789012345678901234567890
    
    fmt.Println(BigNumber)
    
    // 計算も可能
    const Result = BigNumber * 2
    fmt.Println(Result)
}

高精度浮動小数点

package main

import "fmt"

func main() {
    // 非常に高精度な定数
    const Pi = 3.14159265358979323846264338327950288419716939937510
    
    fmt.Printf("%.50f\n", Pi)
    
    // 計算結果も高精度
    const Circumference = 2 * Pi * 10
    fmt.Printf("%.50f\n", Circumference)
}

変数に代入すると精度が制限される

package main

import "fmt"

func main() {
    // 定数は高精度
    const Pi = 3.14159265358979323846264338327950288419716939937510
    
    // float64に代入すると精度が制限される
    var piFloat64 float64 = Pi
    
    fmt.Printf("定数: %.50f\n", Pi)
    fmt.Printf("float64: %.50f\n", piFloat64)
    // float64の精度は約15-17桁
}

8. 実用例

例1: 設定定数

package main

const (
    AppName    = "MyApplication"
    Version    = "1.0.0"
    MaxRetries = 3
    Timeout    = 30  // 秒
)

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

例2: 数学定数

package main

import "fmt"

const (
    Pi  = 3.14159265358979323846
    E   = 2.71828182845904523536
    Phi = 1.61803398874989484820  // 黄金比
)

func main() {
    // 円の面積
    radius := 5.0
    area := Pi * radius * radius
    fmt.Printf("半径%.1fの円の面積: %.2f\n", radius, area)
}

例3: 単位変換

package main

import "fmt"

const (
    // 時間の単位(ナノ秒)
    Nanosecond  = 1
    Microsecond = 1000 * Nanosecond
    Millisecond = 1000 * Microsecond
    Second      = 1000 * Millisecond
    Minute      = 60 * Second
    Hour        = 60 * Minute
    Day         = 24 * Hour
)

const (
    // データサイズの単位
    Byte     = 1
    Kilobyte = 1024 * Byte
    Megabyte = 1024 * Kilobyte
    Gigabyte = 1024 * Megabyte
    Terabyte = 1024 * Gigabyte
)

func main() {
    fmt.Printf("1時間 = %d 秒\n", Hour/Second)
    fmt.Printf("1GB = %d バイト\n", Gigabyte)
}

例4: 列挙型(Enum)風の定数

package main

import "fmt"

type Weekday int

const (
    Sunday Weekday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

func (w Weekday) String() string {
    names := [...]string{
        "日曜日", "月曜日", "火曜日", "水曜日",
        "木曜日", "金曜日", "土曜日",
    }
    return names[w]
}

func main() {
    today := Wednesday
    fmt.Println(today)  // 水曜日
    
    if today == Wednesday {
        fmt.Println("週の真ん中です")
    }
}

例5: ビットフラグの実用例

package main

import "fmt"

type Permission uint

const (
    None Permission = 0
    Read Permission = 1 << iota  // 1
    Write                         // 2
    Execute                       // 4
    Delete                        // 8
)

func (p Permission) String() string {
    var perms []string
    if p&Read != 0 {
        perms = append(perms, "Read")
    }
    if p&Write != 0 {
        perms = append(perms, "Write")
    }
    if p&Execute != 0 {
        perms = append(perms, "Execute")
    }
    if p&Delete != 0 {
        perms = append(perms, "Delete")
    }
    if len(perms) == 0 {
        return "None"
    }
    return fmt.Sprintf("%v", perms)
}

func main() {
    // 権限の組み合わせ
    userPerm := Read | Write
    adminPerm := Read | Write | Execute | Delete
    
    fmt.Println("ユーザー権限:", userPerm)      // [Read Write]
    fmt.Println("管理者権限:", adminPerm)       // [Read Write Execute Delete]
    
    // 権限チェック
    if userPerm&Write != 0 {
        fmt.Println("書き込み権限があります")
    }
}

まとめ: 定数で覚えておくべきこと

定数の6つの種類

  1. 真偽値定数: true, false
  2. ルーン定数: 'A', 'あ'
  3. 整数定数: 42, 0xFF
  4. 浮動小数点定数: 3.14, 1.5e10
  5. 複素数定数: 3+4i
  6. 文字列定数: "Hello"

型なし定数の利点

const x = 42  // 型なし

// 様々な型に使える
var a int = x
var b int64 = x
var c float64 = x

iota の活用

const (
    A = iota  // 0
    B         // 1
    C         // 2
)

const (
    KB = 1 << (10 * iota)  // 1024
    MB                      // 1048576
    GB                      // 1073741824
)

実用的なアドバイス

package main

const (
    // 設定値は定数で
    AppName = "MyApp"
    Version = "1.0.0"
    
    // マジックナンバーを避ける
    MaxRetries = 3
    Timeout    = 30
    
    // 列挙型風に
    StatusActive = iota
    StatusInactive
    StatusDeleted
)

func main() {
    // 定数は変更できない
    // AppName = "NewApp"  // エラー!
    
    // 計算も可能
    const result = MaxRetries * 2  // 6
}

定数は、プログラムの可読性と保守性を高める重要な機能です。マジックナンバー(意味不明な数値)を避け、意味のある名前を付けた定数を使いましょう!

おわりに 

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

よっしー
よっしー

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

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

コメント

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