Golang 入門

Go 基礎文法

Go 言語の文法は、C 言語などの伝統的な言語の影響を受けていますが、その核心となる設計目標は「シンプルさ」「高い可読性」「使いやすさ」にあります。

この章では、パッケージ宣言、インポート、関数、ステートメント、エクスプレッション(式)、そしてコメントなど、Go コードの基礎となる構成要素について包括的に解説します。これらの要素がどのように連携し、最終的に一つの完全な実行可能プログラムとして組み立てられるのかを詳しく見ていきましょう。

1. パッケージ宣言 (Package Declaration)

すべての Go プログラムの冒頭には、必ずパッケージ宣言を含める必要があります。パッケージ (Package) は、Go 言語においてコードを組織化し、再利用するための中心的な手法です。package キーワードを使用して、現在のファイルがどのパッケージに属しているかを指定します。

  • main パッケージ: これは極めて特殊なパッケージです。すべての実行可能プログラムのエントリポイントとなります。Go プログラムを実行すると、システムは自動的に main パッケージ内の main 関数を探して実行します。
  • その他のパッケージ: main 以外のパッケージは、通常、ライブラリ (Libraries) や再利用可能なモジュールとして使用されます。これらには main 関数はなく、主に他のプログラムやパッケージからインポートして使用されることを目的としています。
package main // 現在のファイルが main パッケージに属することを宣言(実行可能プログラムのエントリポイント)

// ライブラリとしてのパッケージ宣言の場合は、通常以下のように記述します:
// package mylibrary

2. インポート (Imports)

import ステートメントは、他のパッケージ内のコードを自分のプログラムに取り込むために使用します。インポートすることで、そのパッケージで定義されている関数、型、変数を使用できるようになります。

  • 標準ライブラリ (Standard Library): Go は非常に豊富な標準ライブラリを持っており、多くの実用的なパッケージを提供しています。例えば、入出力のフォーマットを行う fmt、OS の低レイヤー機能を呼び出す os、ネットワーク通信のための net/http などがあります。
  • サードパーティパッケージ: 他の開発者が作成したパッケージをインポートすることもできます。これらは通常、GitHub などのプラットフォームでホストされています。
  • 一括インポート (Importing Multiple Packages): 丸括弧 () を使用することで、一つの import ステートメントで複数のパッケージを同時にインポートでき、コードをスッキリと保てます。
  • エイリアスインポート (Aliasing Imports): コード内でインポートしたパッケージに「別名(エイリアス)」を付けることができます。これは、異なるパッケージ間で名前が衝突する場合や、パッケージ名が長すぎて簡略化したい場合に非常に便利です。
  • ドットインポート - 非推奨 (Dot Imports): Go はドットインポート(例:import . "mypackage")をサポートしています。これは、そのパッケージ内のすべての公開識別子を現在のファイルのネームスペースに直接取り込みます。しかし、名前の衝突を招きやすく、可読性を著しく低下させるため、通常は推奨されません。
package main

import (
	"fmt"      // fmt パッケージをインポート(入出力のフォーマット用)
	"os"       // os パッケージをインポート(OS関連の機能用)
	m "math"   // math パッケージをインポートし、エイリアス 'm' を付与
)

func main() {
	fmt.Println("Hello, World!")
	fmt.Println(os.Getenv("PATH")) // os パッケージを使用して環境変数を取得する例
	fmt.Println(m.Pi)              // エイリアス 'm' を付けた math パッケージを使用する例
}

3. 関数 (Functions)

関数は Go プログラムの基石です。これらは独立したコードブロックであり、特定のタスクを実行する役割を担います。

  • main 関数: 実行可能プログラムの絶対的なエントリポイントです。引数を受け取らず、戻り値もありません。
  • 関数宣言: func キーワードを使用して関数を宣言します。その後に、関数名、引数リスト(ある場合)、戻り値の型(ある場合)が続きます。
  • 引数と戻り値: 関数は 0 個以上の引数を受け取ることができ、0 個以上の戻り値を返すことができます。
  • 関数本体: 関数の具体的なロジックは、波括弧 {} の中に記述する必要があります。
package main

import "fmt"

// 2つの整数 (int) を引数として受け取り、その和 (int) を返す単純な関数
func add(x int, y int) int {
	return x + y
}

// 引数も戻り値もない関数
func greet() {
	fmt.Println("Hello!")
}

// 複数の値を返すことができる関数 (Go 言語の大きな特徴)
func divide(x int, y int) (int, error) {
	if y == 0 {
		return 0, fmt.Errorf("cannot divide by zero (0で割ることはできません)")
	}
	return x / y, nil
}

func main() {
	sum := add(5, 3)
	fmt.Println("Sum:", sum) // 出力: Sum: 8
    
	greet() // 出力: Hello!
    
	result, err := divide(10, 2)
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("Result:", result) // 出力: Result: 5
	}
    
	result, err = divide(10, 0)
	if err != nil {
		fmt.Println("Error:", err) // 出力: Error: cannot divide by zero (0で割ることはできません)
	} else {
		fmt.Println("Result:", result)
	}
}

4. ステートメントとエクスプレッション

  • ステートメント (Statements): Go コンパイラに対して、アクションを実行するように命じる命令です。
  • エクスプレッション (Expressions): 値、変数、演算子、関数呼び出しの組み合わせであり、計算の結果として必ず一つの値を導き出します。

一般的なステートメントの種類には以下が含まれます:

  • 宣言ステートメント (Declaration Statements): 変数や定数を宣言するために使用します。
  • 代入ステートメント (Assignment Statements): 具体的な値を変数に代入します。
  • 制御フローステートメント (Control Flow Statements): ifforswitch など、コードの実行順序を制御します(詳細は後の章で解説します)。
  • 式ステートメント (Expression Statements): 式を評価します。例えば、関数の呼び出しや数学的な演算などがこれにあたります。
  • 簡易ステートメント (Simple Statements): iffor などのキーワードの直前によく記述される短い操作です。
package main

import "fmt"

func main() {
	// 宣言ステートメント
	var x int
    
	// 代入ステートメント
	x = 10
    
	// 式ステートメント (x + 5 を計算し、結果を y に代入)
	y := x + 5
    
	// プリント文 (これも一種の式ステートメント)
	fmt.Println("x:", x, "y:", y) // 出力: x: 10 y: 15
    
	// 簡易ステートメントを含む if 文 (先に z := x * 2 を実行してから、z > 15 を判断)
	if z := x * 2; z > 15 {
		fmt.Println("z は 15 より大きいです。現在の値:", z) // 出力: z は 15 より大きいです。現在の値: 20
	}
}

5. コメント (Comments)

コメントは、他の開発者(または未来の自分)に向けた説明文です。Go コンパイラはビルド時にすべてのコメントを完全に無視します。

  • シングルラインコメント: // で始まり、その行の終わりまで続きます。
  • マルチラインコメント: /* で始まり、*/ で終わります。複数の行にまたがることができます。
package main

import "fmt"

func main() {
	// これはシングルラインコメントです
	fmt.Println("Hello, World!") // この行のコメントは、このコードが何をしているかを説明します

	/*
	   これはマルチラインコメントです。
	   複数の行にわたって記述することができ、
	   複雑なロジックを説明するのに適しています。
	*/
	fmt.Println("これは別の行のコードです。")
}

6. コードブロックとスコープ

コードブロック (Code Block) は、一対の波括弧 {} で囲まれたステートメントのグループです。コードブロックはコードをまとめるだけでなく、変数のスコープを決定します。

  • スコープ (Scope): 変数がコード内でアクセスおよび使用可能な範囲を指します。コードブロックの内部で宣言された変数は、そのブロック内部からのみアクセス可能です。
  • ネストされたブロックとシャドウイング (Shadowing): コードブロックはネスト(入れ子)にすることができます。外側のブロックで宣言された変数は、内側のブロックでも使用できます。ただし、内側のブロックで外側と同じ名前の変数を宣言すると、内側の変数が外側の変数を「隠蔽(シャドウイング)」します。内側のブロックでは、内側の新しい変数を操作することになります。
package main

import "fmt"

func main() {
	x := 10 // x は外側のコードブロックで宣言
	{
		y := 20 // y は内側のコードブロックで宣言
		fmt.Println("内側ブロックの x:", x) // ここでは外側の x にアクセス可能
		fmt.Println("内側ブロックの y:", y) // ここでは内側の y にアクセス可能
	}
    
	fmt.Println("外側ブロックの x:", x) // ここでは x にアクセス可能
	// fmt.Println("外側ブロックの y:", y) // この行のコメントを外すとエラーになります! y はここでは見えません(スコープ外)

	{
		x := 30 // 注意:内側で全く新しい変数 x を宣言し、外側の x を「シャドウイング」します
		fmt.Println("内側の x:", x) // 出力: 内側の x: 30
	}
    
	fmt.Println("外側の x:", x) // 出力: 外側の x: 10 (最外側の x は変更されていません)
}

7. 命名規則

Go 言語には非常に厳格かつ実用的な命名規則があり、これによりすべての Go コードが高い統一性を保っています。

  • パッケージ名 (Package Names): 短く、すべて小文字で、かつ内容を適切に表すものであるべきです(例:timehttp)。
  • 変数名と関数名: 内容を説明する名前を用い、キャメルケース (camelCase) を使用します(例:myVariableNamecalculateSum)。
  • 定数名 (Constants): 通常、すべて大文字を使用し、単語間をアンダースコアで区切ります(例:MAX_VALUE)。
  • 公開・非公開(大文字・小文字ルール): これは Go 言語の最も重要な特徴の一つです!
    • 先頭が大文字: 変数、関数、または型の名前が大文字で始まる場合(例:MyPublicFunction)、それは公開 (Exported) されており、他のパッケージから呼び出すことができます。
    • 先頭が小文字: 小文字で始まる場合(例:myPrivateVariable)、それは非公開 (Unexported) であり、そのパッケージ内部でのみ使用可能です。
package main

import "fmt"

// 定数
const MAX_SIZE = 100

// 非公開変数 (未エクスポート、外部パッケージからはアクセス不可)
var myVariable int

// 公開変数 (エクスポート済み、外部パッケージからアクセス可能)
var MyPublicVariable string

// 非公開関数 (未エクスポート)
func myFunction() {
	fmt.Println("これは非公開関数です")
}

// 公開関数 (エクスポート済み)
func MyPublicFunction() {
	fmt.Println("これは公開関数です")
}

func main() {
	myVariable = 10
	MyPublicVariable = "Hello"
	myFunction()
	MyPublicFunction()
}