Go言語で複数戻り値の受け取りで既存変数の再宣言?
非常に分かりにくいタイトルになっていますが、Go言語では変数の宣言を行うには、
var i int var i = 0 i := 0
など複数の宣言方法があります。当然同じスコープ上で複数回宣言しようとすると、
no new variables on left side of := i redeclared in this block previous declaration at .\error_sample.go:11 // 丁寧に既に宣言されている場所を教えてくれる
のように怒られます。
普通にコードを書かれている方々にとっては当たり前のことだと思うのですが、ずっと気になっていたことがあったので確かめてみました。
Go言語では複数戻り値を使用できるので、関数やメソッドの戻り値として、
func function1() (int, error) { return 1, errors.New("エラーです") }
のような書き方をすることがよくあります。呼び出し側は、
i, err := function1()
のように書いたりします。
では上の呼び出しを行った後に、
j, err := function1()
と呼び出したらどうなのでしょう?
jは新規の変数なので問題なし、errは直前でも使用されているので再宣言なのでエラーになるのかというとエラーにはなりません。
ということは、jだけ新規に宣言されて、errは使いまわされているのか、それとも以前のことはなかったことにしてerrも新規に宣言されていることになるのか。
という訳で検証してみたのが以下のコードです。
package main import ( "fmt" ) func main() { err := function1() fmt.Println(err, &err) i, ii, err := function2() fmt.Println(i, &i, ii, &ii, err, &err) i, ii, err = function3() fmt.Println(i, &i, ii, &ii, err, &err) j, ii, err := function4() fmt.Println(j, &j, ii, &ii, err, &err) j, jj, err := function4() fmt.Println(j, &j, jj, &jj, err, &err) fmt.Println(err, &err) } func function1() error { return fmt.Errorf("error1") } func function2() (int, int, error) { return 2, 20, fmt.Errorf("error2") } func function3() (int, int, error) { return 3, 30, fmt.Errorf("error3") } func function4() (int, int, error) { return 4, 40, nil }
実行結果は以下のとおりです。
error1 0xc0820001e0 2 0xc082000230 20 0xc082000238 error2 0xc0820001e0 3 0xc082000230 30 0xc082000238 error3 0xc0820001e0 4 0xc082000290 40 0xc082000238 <nil> 0xc0820001e0 4 0xc082000290 40 0xc082000298 <nil> 0xc0820001e0 <nil> 0xc0820001e0
結果を見て分かるように新規変数とともに既存変数を宣言していますが、既存変数のアドレスは同じです。
どうやら := 演算子で受け取る複数戻り値のうち1つでも新しい変数が含まれていたらエラーにはならず、既存の変数は使いまわされるようです。
追記
@_makoto_sato_ さんからご指摘を受けました(サンプルソース)が、あくまで同じスコープ上での話です。
異なるスコープでは別の変数として宣言されます。