関数
関数宣言
func name(parameter-list) (result-list) {
body
}
結果リストを省略すると、結果を返さない関数になる。結果リストが存在する場合は必ずreturn
文で終了しなければならない。
シグネチャ(signature)
関数の型はその関数のsignatureと呼ばれることがあり、パラメータの型の型と結果の列の型が同じであれば、同じ型、すなわち同じsignatureを持つ。パラメータの名前と結果の名前は関数の方には影響しない。パラメータや結果をまとめた形式で宣言しても影響しない。
- すべての関数呼び出しは、パラメータgは宣言された順にそれぞれに対する引数を提供しなければならない
- デフォルトパラメータという概念は存在しない
- パラメータは関数本体内ではローカル変数であり、その初期値は呼び出し元が提供した引数の値に設定されている。
注意点
- 引数は値で渡されているので、関数はそれぞれの引数のコピーを受け取る。基本的にコピーに対する修正は、呼び出し元には影響しないが、引数にポインタ等を渡した場合、引数によって間接的に参照されている変数に対して関数側で修正を行うと、呼び出し元の変数に影響が出てしまう。
再帰
関数は自分自身を直接的あるいは間接的に呼び出すことができる。
func sum(num int) (totalNum int) {
if num == 0 {
return 0
}
return num + sum(num - 1)
}
func main() {
for n := 0; n <= 10; n++ {
fmt.Println(sum(10))
}
}
複数戻り値
- 結果は値のタプル
resp, err := http.Get(url)
- 値の一つを無視するためには、その値をブランク識別子へ代入する
links, _ := findLinks(url)
エラー処理について
関数がエラーを返した場合、そのエラーを検査して適切に対応するのは呼び出し元の責務。
エラーを返してきた場合の対応
- エラーを呼び出し元に返す
resp, err := http.Get(url)
if err != nil {
return nil, err
}
- 元のエラーに追加の情報を付与して返す
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("Parsing %s as HTML : %v", url, err)
}
fmt.Errorf
関数はfmt.Sprintf
を使ってエラーメッセージをフォーマットし、新たなerror値を返す
- 処理を進めるのが不可能な場合、エラーを表示してプログラムを停止させる
mainパッケージのみに限定するべき
if err := WaitForServer(url); errr != nil {
fmt.Fprintf(os.Stderr, "Site is down: %v\n", err)
os.Exit(1)
}
以下log.Fatelf
でも同じことが可能
if err := WaitForServer(url); errr != nil {
log.Fatelf("Site is down: %v\n", err)
}
関数値
Goでは関数はファーストクラス値
であり、他の値と同様に型を持ち、変数に代入したり、関数へ渡したり、関数から返したりできる。
- 関数値は比較は出来ない
- マップのキーとして使用できない
無名関数
名前付き関数はパッケージレベルで宣言できるが関数値を表す関数リテラルは全ての式内で使える。関数リテラルは式であり、その値は無名関数
と呼ばれる。
func squares() func() int {
var x int
return func() int {
x++
return x * x
}
}
func main() {
f := squares()
fmt.Println(f()) // 1
fmt.Println(f()) // 4
fmt.Println(f()) // 9
fmt.Println(f()) // 16
}
ループ変数の補足
ループ変数により作成される全ての関数値は、同じ変数を「捕捉」して共有する(特定の瞬間の値ではなく、アドレス化可能なメモリ位置を共有する)
可変個引数関数
可変個の引数で呼び出すことができる関数
func sum(vals ...int) {
total := 0
for _, val := range vals {
total += val
}
return total
}
fmt.Println(sum()) // 0
fmt.Println(sum(3)) // 3
fmt.Println(sum(1, 2, 3, 4)) // 10
遅延関数呼び出し
defer
文を使用することで、それを呼び出した関数が終了する際に実行すべき処理を記述することができる。
func main() {
defer fmt.Println("A")
fmt.Println("B")
}
// mainの最後にAが出力される
複数書くと、最初にdefer
された行が一番最後に実行される
パニック
Goランタイムが実行時にエラーを検出した場合にはpanic
になる。通常の実行は停止し、そのゴルーチン内の全ての遅延関数の呼び出しが行われ、プログラムはログメッセージを表示してクラッシュする。そのログメッセージにはパニック値と、パニック時に動作していた関数呼び出しを示しているスタックトレースが含まれている。
リカバー
recover
はpanicの現状を終了させてパニック値を返す。パニック担っていない場合はnil
を返す。
予期できるエラーの場合は、recoverは使うべきではない、通常errorで実装すること。