20210417のGoに関する記事は3件です。

Golang 〜Defer, Panic, and Recover〜

Defer 関数呼び出しをリストに Push する。周囲の関数を実行後、リストに貯まった関数が実行される。 Defer は、様々な Clean up 処理に使用される。 サンプルコード ファイルをコピーする下記のコードを例にする。 func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } dst, err := os.Create(dstName) if err != nil { return } written, err = io.Copy(dst, src) dst.Close() src.Close() return } このサンプルコードは動くものの、バグが潜在する。os.Create(dstName) が失敗すると、コピー元のファイルを close することができない。 このバグは、以下のように src に対して明示的に Close() を呼び出すことで解決できる。 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) } Defer によって、各ファイルをオープン後に close することが保証される。 3つのルール 1. Defer 関数の引数は、Defer ステートメントが評価される時に評価される main.go func a() { i := 0 defer fmt.Println(i) i++ return } func main() { b() } $ go run main.go 0 上記を実行すると、0が出力される。iは、Println()が call された際に評価されることがわかる。 2. Defer 関数の呼び出しは、 LIFO(Last In First Out) にて実行される main.go func b() { for i := 0; i < 4; i++ { defer fmt.Println(i) } } func main() { b() } $ go run main.go 3 2 1 0 3. Defer 関数は、関数の戻り値を読み取り、新しい値を割り当てることができる main.go func c() (i int) { defer func() { i++ }() return 1 } func main() { fmt.Println(c()) } $ go run main.go 2 上記の例では、c()の返り値 i に1を足す Defer 関数を作成している。よって、2が返ってくる。 3のルールは、エラー時の挙動を決定するにあたり、非常に有用である。 Panic Panic() は、通常の制御の流れを止めて Panic を開始する組み込み関数である。関数FがPanicを呼び出すと、Fの実行が停止し、F内のすべての Defer 関数が正常に実行されてから、Fは呼び出し元に戻る。 Caller にとって、Fはパニックへの呼び出しのように動作する。プロセスは、現在の goroutine (Goのランタイムによって管理される軽量スレッド)内のすべての関数が戻るまで Stack を上っていく。戻ると、プログラムがクラッシュする。 Panic は、 Panic を直接呼び出すことによって開始できる。また、範囲外の配列アクセスなどのランタイムエラーが原因で発生することもある。 Recover Recover は、 Panic 状態の goroutine の制御を取り戻す組み込み関数である。 Recover は、 Defer 関数内でのみ使用可能。通常の実行中、 Recover の呼び出しは nil を返し、他の効果はない。現在の goroutine が Panic になっている場合、Recoverの呼び出しは Panic に与えられた値をキャプチャし、通常の実行を再開する。 サンプルコード main.go 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 nomally 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) } $ go run main.go 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. Recover() がなかった場合、以下のように goroutine のスタック最上部まで戻り、プログラムを終了させる。 main.go 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 nomally 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) } $ go run main.go 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 panic: 4 (StackTrace は省略。。。) Panic と Recover の実例として、Go標準ライブラリのjsonパッケージを参照すると良い。jsonパッケージは、一連の再帰関数を使用してインターフェースをエンコードする。値のトラバース中にエラーが発生した場合、 Panic() が呼び出されてスタックが最上位の関数呼び出しに巻き戻され、 Panic から回復して適切なエラー値が返される(encode.go)。 Goライブラリの規則では、パッケージが内部で Panic を使用している場合でも、その外部 API は明示的なエラー戻り値を表示する、としている。 他に、 Defer の使用例として、 Mutex のリリースや、フッターの出力などがある。 mu.Lock() defer mu.Unlock() printHeader() defer printFooter() 参考 Defer, Panic, and Recover
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Fatal error: read tcp 127.0.0.1:xxxx->127.0.0.1:yyyy: read: connection reset by peer

事象 サーバ・クライアント間でTCP/IPコネクションを確立している状態でクラアントからサーバにデータを送信すると以下のエラーが出てきた。 connection reset by peer 原因 サーバからクライアントにRST(Reset TCP)パケットが送信されてコネクションがブチ切られている。 電話に例えると、発信者(クライアント)から受信者(サーバ)に電話した時に相手側が即効電話を切っていると同じ。 RSTとは When an unexpected TCP packet arrives at a host, that host usually responds by sending a reset packet back on the same connection. A reset packet is simply one with no payload and with the RST bit set in the TCP header flags. ①データ送信(クライアント→サーバ) ②RSTパケット送信(サーバ→クライアント) ③コネクション終了 Stack Over Flowでの説明がわかりやすい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

$GOPATH/go.mod exists but should not

事象 goファイルを実行する go run xxx.go 以下のエラーが出てきた。 $GOPATH/go.mod exists but should not 原因 $GOPATHで指定されているディレクトリ配下にxxx.modがあることが原因。 mvコマンドで適当な場所にこのファイルを移動しましょう。 因みにxxx.modファイルはgoモジュールを管理してくれるファイルです。 #カレントディレクトリにgo.modファイルを移動。 mv $GOPATH/go.mod ./
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む