Go言語のデフォルト値について解説
Go言語は変数宣言時に各型ごとに定義されたデフォルト値が自動設定される仕組みを持っています。
この記事では、初期化を省略した場合の動作や基本的なコード例を通じて、シンプルに解説します。
Go言語のデフォルト値の基本
Go言語では、変数を宣言するときに初期値を明示しなくても、型ごとにあらかじめ定められた値が自動的に設定されます。
これにより、未初期化状態でありながらも、変数は決まった状態で扱えるため、予期せぬ動作を防ぐ助けになっています。
変数宣言時に自動設定される仕組み
変数を宣言すると、Goのコンパイラはその型に合わせたゼロ値を各変数に設定します。
たとえば、数値型なら 0、文字列なら空文字、bool型なら false が与えられます。
これにより、宣言後すぐに変数を使用してもエラーにならず、決まった初期状態から処理を開始することが可能です。
明示的初期化との違い
明示的に初期値を与える場合とデフォルト値との大きな違いは、変数の意図する状態を正確に表現できる点です。
明示的初期化は、変数に固有の意味や目的の値を設定するために用いられますが、デフォルト値はあくまで型に沿った基本の状態を保証するものです。
そのため、実際の用途に応じて、必要に応じた初期化とデフォルト値の自動設定のどちらを使うかを判断する必要があります。
基本データ型のデフォルト値
Goでは、数値、文字列、論理値といった基本データ型に対して、それぞれのゼロ値が設定されます。
以下に各型のデフォルト値について詳しく説明します。
数値型の初期値
数値型は整数型や浮動小数点型に分けられ、それぞれ異なるゼロ値が設定されます。
整数型(int、uint)の初期値
整数型(int, int32, uintなど)のデフォルト値は 0 です。
整数の変数を宣言しただけの場合、明示的に値を代入しなくても、内部では 0 として扱われます。
浮動小数点型(float)の初期値
浮動小数点型float32 や float64の場合も、デフォルト値は 0.0 になります。
どちらの場合も、計算などで使用する前に初期化されていなくても基本的な数値として正しく扱える状態です。
文字列型と論理型の初期値
基本データ型のうち、文字列と論理値は特に注意深く扱う必要があります。
stringの初期値
string型の変数は、初期化しなかった場合、空文字列 "" が設定されます。
空文字列は文字が存在しない状態であり、文字列操作を行う際の初期状態として利用されます。
boolの初期値
bool型のデフォルト値は false です。
このため、論理判断に使用する変数を宣言するだけで、予期せぬ真偽判定のエラーを防ぐ効果があります。
複合型のデフォルト値
Go言語の複合型は配列、スライス、マップ、構造体、ポインタなどさまざまな形態があります。
それぞれの型でデフォルト値の動作が異なるため、用途に応じた理解が必要です。
配列とスライスの初期状態
配列とスライスは、基本要素のデフォルト値を保持する形で自動的に初期化されます。
配列の初期値
配列は固定長で宣言され、各要素にはその型のゼロ値が設定されます。
たとえば、整数型の配列を宣言すれば、すべての要素が 0 に初期化されます。
スライスの自動初期化
スライスは、宣言時に nil であるため、要素の追加や明示的初期化を行わない限り、実際の格納スペースは確保されません。
しかし、make関数を使うと、内部の配列が作られ要素に対してデフォルト値が設定されます。
マップと構造体の初期値
マップと構造体は、複数の値をまとめて管理するための型ですが、初期状態についても標準のルールがあります。
mapの初期化状態
map型は、宣言だけでは nil となるため、利用する前に make関数で初期化する必要があります。
nilマップに要素の追加を試みると、ランタイムエラーとなるため注意が必要です。
構造体のフィールド毎の初期値
構造体を宣言すると、各フィールドにはそのフィールドの型に応じたゼロ値が設定されます。
たとえば、整数フィールドなら 0、文字列フィールドなら空文字、boolフィールドなら false といった具合です。
フィールドごとに明示的な値を設定することで、任意の初期状態を決めることも可能です。
ポインタ型の初期状態
ポインタ型の変数は、宣言時に nil が設定されます。
nilポインタとは、どのメモリアドレスも指し示していない状態のことであり、ポインタを使ってメモリ操作を行う前に、適切なアドレスに初期化する必要があります。
コード例で確認するデフォルト値の動作
以下では、実際のコード例を使って、各型に設定されるデフォルト値の動作を確認します。
シンプルな変数宣言例
次のコードは、各基本データ型の変数を宣言した後、デフォルト値がどのような結果となるのかを確認するシンプルな例です。
package main
import (
	"fmt"
)
func main() {
	// 整数型の変数宣言 (デフォルト値は 0)
	var intValue int
	// 浮動小数点型の変数宣言 (デフォルト値は 0.0)
	var floatValue float64
	// 文字列型の変数宣言 (デフォルト値は "")
	var stringValue string
	// 論理型の変数宣言 (デフォルト値は false)
	var boolValue bool
	// 配列の宣言 (各要素は整数型のデフォルト値 0)
	var arrayValue [3]int
	// スライスの宣言 (nil状態)
	var sliceValue []int
	// マップの宣言 (nil状態)
	var mapValue map[string]int
	// 構造体の宣言 (各フィールドにはその型のゼロ値が設定される)
	type SampleStruct struct {
		Number int
		Text   string
		Flag   bool
	}
	var structValue SampleStruct
	// ポインタ型の宣言 (nil状態)
	var pointerValue *int
	fmt.Println("intValue:", intValue)
	fmt.Println("floatValue:", floatValue)
	fmt.Println("stringValue:", stringValue)
	fmt.Println("boolValue:", boolValue)
	fmt.Println("arrayValue:", arrayValue)
	fmt.Println("sliceValue:", sliceValue)
	fmt.Println("mapValue:", mapValue)
	fmt.Println("structValue:", structValue)
	fmt.Println("pointerValue:", pointerValue)
}intValue: 0
floatValue: 0
stringValue:
boolValue: false
arrayValue: [0 0 0]
sliceValue: []
mapValue: map[]
structValue: {0  false}
pointerValue: <nil>各型の実行結果の確認
次のサンプルコードでは、各変数の値をより詳細に確認するために、型ごとに区切って出力を行います。
各出力結果から、Go言語のデフォルト値の動作を把握することができます。
package main
import (
	"fmt"
)
func main() {
	// 数値型のデフォルト値確認
	var anInt int           // 0
	var aUint uint          // 0
	var aFloat float32      // 0.0
	// 文字列型と論理型のデフォルト値確認
	var aString string      // ""
	var aBool bool          // false
	// 複合型:配列、スライス、マップ、構造体、ポインタ
	var anArray [2]int      // [0, 0]
	var aSlice []float64    // nil (コンソール出力では [] と表示される場合あり)
	var aMap map[string]string  // nil (コンソール出力では map[] と表示)
	type Person struct {
		Name   string
		Age    int
		Active bool
	}
	var aStruct Person      // { "", 0, false }
	var aPointer *Person    // nil
	fmt.Println("== 数値型 ==")
	fmt.Println("anInt:", anInt)
	fmt.Println("aUint:", aUint)
	fmt.Println("aFloat:", aFloat)
	fmt.Println("== 文字列・論理型 ==")
	fmt.Println("aString:", aString)
	fmt.Println("aBool:", aBool)
	fmt.Println("== 複合型 ==")
	fmt.Println("anArray:", anArray)
	fmt.Println("aSlice:", aSlice)
	fmt.Println("aMap:", aMap)
	fmt.Println("aStruct:", aStruct)
	fmt.Println("aPointer:", aPointer)
}== 数値型 ==
anInt: 0
aUint: 0
aFloat: 0
== 文字列・論理型 ==
aString:
aBool: false
== 複合型 ==
anArray: [0 0]
aSlice: []
aMap: map[]
aStruct: { 0 false}
aPointer: <nil>デフォルト値利用時の注意事項
デフォルト値を利用する際には、いくつか注意すべき点があります。
意図しない挙動を回避するために、各種型の特性を正しく理解することが大切です。
意図しない挙動の回避
Go言語の変数は自動でゼロ値が設定されますが、nil状態のスライスやマップ、ポインタに対して直接要素の追加やメソッド呼び出しを行うと、ランタイムエラーになる可能性があります。
たとえば、nilのマップに対して要素を追加しようとするとエラーが発生するため、必ず make関数を使用して初期化する必要があります。
明示的初期化との使い分けの検討
プログラムの意図や設計に応じて、デフォルト値の利用と明示的な初期化のどちらが適しているかを検討する必要があります。
意図しない値の混入を防ぐため、場合によっては明示的に初期値を設定しておくことが望まれる場面もあります。
たとえば、構造体の各フィールドに特定の初期値が必要な場合には、コンストラクタ関数を用いるなどの工夫が重要です。
まとめ
この記事では、Go言語の変数宣言時に自動的に設定されるデフォルト値の基本や各型の初期値、コード例を通してその動作を解説しました。
各型のゼロ値の仕組みや、意図しない挙動を回避するための注意点を簡潔に把握できる内容です。
ぜひ、実際にサンプルコードを動かして内容を確認してみてください。