はじめに
Go の勉強のため、A Tour of Goに取り組んでいる
今回はFlow control statements: for, if, else, switch and defer
章について、学んだことを記していく
まとめ記事はこちら
使えそうなスニペット
※筆者は VSCode を使用
for
初期値、条件式、後処理、スコープ内部の順にカーソルが移動する
for i := 0; i < count; i++ {
}
if
条件式、スコープ内部にカーソルが移動する
el
-> else { }
で else ブロックを追加できる
if condition {
}
ei
一発でも作れる
if condition {
} else {
}
switch
条件式、case、スコープ内部にカーソルが移動する
cs
-> case condition:
で条件を追加できる
switch expression {
case condition:
}
df
defer func()
pn
panic("")
recover
こちらは存在しなかったので、自作した
(Preferences: Configure User Snippets
コマンドから作成可能)
出力されるのは、
defer func() {
if r := recover(); r != nil {
}
}()
User Snippet の Json は、
"defer recover": {
"prefix": "recover",
"body": [
"defer func() {",
" if r := recover(); r != nil {",
" $1",
" }",
"}()",
],
"description": "defer recover"
}
ページごとの補足
For continued
for 文で定義した変数を、条件式で使わなくても良い
x := 1
for i := 0; 0 < x; {
fmt.Println(i) // 流石にスコープ内で使わないと怒られる
}
for で定義する変数は、定義済みでも良い
x := 1
for x = 0; x < 10; x++ {
fmt.Println(x)
}
If and else
- if ステートメントで宣言された変数は、
else if
ブロック内でも使える -
else if
ステートメントで宣言された変数も、後続のelse if
、else
ブロック内でも使える - 同じ名前の変数を宣言した場合、上書きされる(=スコープがネストされる?)
x := 1 // 1,2,3,4
if y := 1; x == 1 {
fmt.Println("x == 1 : y = ", y)
} else if y := 2; x == 2 {
fmt.Println("x == 2 : y = ", y)
} else if y := 3; x == 3 {
fmt.Println("x == 3 : y = ", y)
} else {
fmt.Println("x > 3 : y = ", y)
}
// x == 1 : y = 1
// x == 2 : y = 2
// x == 3 : y = 3
// x > 3 : y = 3
Switch with no condition
Switch の case の条件式では変数宣言ができないため、例えば上述したif-else
は、置き換えることができなさそう
Defer
A Tour of Go
では言葉少なに紹介されているため、Defer, Panic, and Recover(日本語訳は こちら)の内容がメインになる
defer の使い方の 1 つは、他言語でいうfinally
(Python でいうwith
、Kotlin でいうuse
の代わりにもなる)
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
他にも panic, recover があり、それぞれ以下のように対応しそう
- panic: throw error
- recover: catch
以下はエラーハンドリングについて、公式のサンプルプログラム
package main
import "fmt"
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
これを実行すると、以下のようになる
Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.
少し癖がありますが、使い慣れると便利そう
何度もtry-catch
を書かずに済みそうだ
雑感
このあたりは他言語と似通った仕様が多いので、難しい印象はない(最後の defer は別だが)
Go 言語は言語の仕様変更・追加が少ないので、数年前の記事が当てになるのが良い