Go 言語入門基礎学習ノートの Go 言語の変数

変数#
Go 言語の変数名は、アルファベット、数字、アンダースコアで構成されており、最初の文字は数字であってはいけません。
変数を宣言する一般的な形式は、varキーワードを使用します:
var identifier type
複数の変数を一度に宣言することもできます:
var identifier1, identifier2 type
Go 言語の習慣は、変数名を前に、データ型を後に置くことです。
Go 言語で変数を宣言したら、必ず使用しなければなりません。そうしないとエラーになります。
単一変数の宣言#
- 変数の型を指定する
 
var v_name v_type
v_name = value
初期化しない場合、変数はデフォルトでゼロ値になります。
ゼロ値とは、変数が初期化されていないときにシステムがデフォルトで設定する値です。
- 数値型のゼロ値(complex 64/128 を含む)は **
0** です。 - ブール型のゼロ値は **
false** です。 - 文字列のゼロ値は **
""**(空文字列)です。 - その他のいくつかの型は **
nil** です: 
var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error // errorはインターフェースです
- 値に基づいて変数の型を自動判定する
 
Python に似ていて、明示的に型を宣言する必要はなく、自動的に推論されます。
var v_name = value
:=を使用して変数を宣言する
この短い変数宣言は通常関数内部で使用され、グローバル変数には使用できません。
変数がすでにvarで宣言されている場合、再度:=で変数を宣言するとコンパイルエラーが発生します。
同様に、変数を:=で宣言して値を割り当てた後、再度宣言することはできません。
var intVal int 
intVal := 1 
// この時点でコンパイルエラーが発生します。なぜならintValはすでに宣言されているからです。
intVal := 1は以下のように相当します:
var intVal int 
intVal = 1
// または
var intVal int = 1
複数変数の宣言#
- 非グローバル変数の宣言
 
同じ型の複数の変数は、一般的にこの方法で非グローバル変数を宣言します。
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
- 値に基づいて変数の型を自動判定する
 
Python に似ていて、明示的に型を宣言する必要はなく、自動的に推論されます。異なるデータ型を混合することができます。
var vname1, vname2, vname3 = v1, v2, v3 
:=を使用して変数を宣言する
:=の左側にある変数は、すでに宣言されているものであってはいけません。そうでないとコンパイルエラーが発生します。
この宣言形式は関数内でのみ使用でき、異なるデータ型を混合することができます。
vname1, vname2, vname3 := v1, v2, v3
- グローバル変数の宣言
 
このような分解キーワードの書き方は、一般的にグローバル変数を宣言するために使用されます。初期化しない場合はデフォルト値になります。
var (
    vname1 v_type1 = v1
    vname2 v_type2
)
例:
package main
import "fmt"
var x, y int
var (  // このような分解キーワードの書き方は、一般的にグローバル変数を宣言するために使用されます。
    a int
    b bool
)
var c, d int = 1, 2
var e, f = 123, "hello"
// この宣言形式は関数体内でのみ使用できます。
//g, h := 123, "hello"
func main(){
    g, h := 123, "hello"
    fmt.Println(x, y, a, b, c, d, e, f, g, h)
}
値型と参照型#
int、float、bool、string などの基本型はすべて値型に属し、これらの型の変数はメモリ内の値を直接指します。
参照型:スライス、ポインタ、マップ、チャネル、関数

等号=を使用して 1 つの変数の値を別の変数に割り当てるとき、例えばj = i、実際にはメモリ内でiの値がコピーされます。

&iを使用して変数iのメモリアドレスを取得できます。例えば:0xf840000040、毎回アドレスは異なる可能性があります。
値型変数の値はヒープに保存されます。
メモリアドレスはマシンによって異なる場合があり、同じプログラムが異なるマシンで実行された場合でも異なるメモリアドレスが生成されることがあります。なぜなら、各マシンは異なるメモリレイアウトを持ち、位置の割り当ても異なる可能性があるからです。
より複雑なデータは通常複数の単語を使用する必要があり、これらのデータは一般的に参照型で保存されます。
参照型の変数r1は、r1の値が存在するメモリアドレス(数字)またはメモリアドレス内の最初の単語の位置を保存します。
このメモリアドレスはポインタと呼ばれ、このポインタは実際には別の値の中に存在します。
同じ参照型のポインタが指す複数の単語は、連続したメモリアドレスに存在することがあります(メモリレイアウトは連続しています)。これは計算効率が最も高いストレージ形式です。また、これらの単語をメモリ内に分散して保存することもでき、各単語が次の単語のメモリアドレスを指示します。
代入文r2 = r1を使用すると、参照(アドレス)のみがコピーされます。
もしr1の値が変更された場合、その値のすべての参照は変更された内容を指し示します。この例では、r2も影響を受けます。
注意事項#
短い形式で:=代入演算子を使用する
変数の初期化時に型を省略し、システムが自動的に推論することができます。宣言文にvarキーワードを書くことは実際には少し冗長です。したがって、これらをa := 50またはb := falseのように短縮することができます。
a と b の型(int と bool)はコンパイラによって自動的に推論されます。
これは変数を使用する推奨形式ですが、関数体内でのみ使用でき、グローバル変数の宣言と代入には使用できません。:=演算子を使用すると、新しい変数を効率的に作成することができ、これを初期化宣言と呼びます。
同じコードブロック内で、同じ名前の変数に対して初期化宣言を繰り返すことはできません。なぜなら、以前に述べたように:=は宣言 + 代入に相当し、コンパイラはエラーno new variables on left side of :=を提示します。しかし、a = 20は可能です。なぜなら、これは同じ変数に新しい値を割り当てるからです。
もし変数 a を定義する前に使用すると、コンパイルエラーundefined: aが発生します。
もしローカル変数を宣言したが、同じコードブロック内で使用しなかった場合も、同様にコンパイルエラーが発生します。
package main
import "fmt"
func main() {
   var a string = "abc"
   fmt.Println("hello, world")
}
このコードをコンパイルしようとすると、エラーa declared but not usedが表示されます。
したがって、単に a に値を割り当てるだけでは不十分で、その値は使用されなければなりません。しかし、グローバル変数は宣言されていても使用しないことが許可されています。
複数の変数を同じ行で宣言および代入できます。
var a, b int
var c string
a, b, c = 5, 7, "abc"
// または直接短い形式を使用
a, b, c := 5, 7, "abc"
右側のこれらの値は、左側の変数に同じ順序で割り当てられます。これを並行または同時代入と呼びます。
2 つの変数の値を交換したい場合は、単にa, b = b, aを使用できます。2 つの変数の型は同じでなければなりません。
空白識別子_も値を捨てるために使用されます。例えば、値5は_, b = 5, 7の中で捨てられます。
_は実際には書き込み専用変数であり、その値を取得することはできません。これは、Go 言語ではすべての宣言された変数を使用しなければならないが、時には関数から得られたすべての戻り値を使用する必要がない場合があるためです。
並行代入は、関数が複数の戻り値を返すときにも使用されます。