golangいまどき例外ないの頭おかしいって思ってたけどようするにgoroutineと例外がうまくいかないからgoroutineのほう取って例外捨てたってことかねえ。
— Urabe, Shyouhei (@shyouhei) April 15, 2014
FAQ に書いてあります。
Why does Go not have exceptions? - Frequently Asked Questions (FAQ) - The Go Programming Languagetry-catch-finally イディオムはコードを複雑にするし、たかがファイルのオープンに失敗しただけのエラーに過剰なラベル付を強要する。 golang では複数の値を返す事が出来るので、一般的には戻り値の最後にエラーを付けて返す。
We believe that coupling exceptions to a control structure, as in the
try-catch-finally
idiom, results in convoluted code. It also tends to encourage programmers to label too many ordinary errors, such as failing to open a file, as exceptional.Go takes a different approach. For plain error handling, Go's multi-value returns make it easy to report an error without overloading the return value. A canonical error type, coupled with Go's other features, makes error handling pleasant but quite different from that in other languages.
Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. The recovery mechanism is executed only as part of a function's state being torn down after an error, which is sufficient to handle catastrophe but requires no extra control structures and, when used well, can result in clean error-handling code.
http://golang.org/doc/faq#exceptions
と書いてあります。
これは頭悪いな。golang作者は自分には例外が使いこなせませんって言ってるだけだ http://t.co/ePRhLBkoXW
— Urabe, Shyouhei (@shyouhei) April 16, 2014
何度読んでも「例外が使いこなせません」とは読めません。すいません。複雑になるのは良くないという話はそもそもレイヤの違う話だと思いますよ。まぁ FUD なツィートで人気のある方なので、ディスカッションするつもりもないですが。バグが発生する多くの問題は、エラーハンドリングが正しくされていない事が原因だったりします。golang の場合は戻り値が複数あった場合、戻り値を得るには必ず全ての戻り値を変数にバインドしないといけない仕様になっています。つまりは戻り値が欲しければ、error も取れという事になりますね。
package main
import (
"fmt"
)
func doSomething(input int) (result string, err error) {
switch {
case input < 0:
return "", fmt.Errorf("%d is negative value", input)
case input < 50:
return "less than 50", nil
default:
return "greater than 50", nil
}
}
func main() {
if r, err := doSomething(20); err != nil {
fmt.Println(err)
} else {
fmt.Println(r)
}
}
golang では if の else ブロック内でも if 句内で宣言した変数が参照出来ます。Java の try-catch-finally だと try 内で宣言した変数は catch ブロックでは参照出来ません。それを回避する為に try よりも上部に持っていって... などというのは良くある話ですし、ネストだらけの try-catch-finally も良く見ますね。golangで複数の種類のエラーが起きる関数を、呼び出し側で適切にハンドリングする時ってどうやって識別してんだろ?
— Mr. Fiber (@repeatedly) April 15, 2014
golang でも細かいエラーハンドリングは出来ます。この辺はドキュメントを読むと書いてあります。例えば os.Open
ならば
func Open - The Go Programming Languageエラーがあった場合には PathError が返ります。
Open opens the named file for reading. If successful, methods on the returned file can be used for reading; the associated file descriptor has mode O_RDONLY. If there is an error, it will be of type *PathError.
http://golang.org/pkg/os/#Open
package main
import (
"log"
"os"
"syscall"
)
func main() {
f, err := os.Open("not-found-file.txt")
if err != nil {
pathErr := err.(*os.PathError)
errno := pathErr.Err.(syscall.Errno)
if errno == syscall.ENOENT {
log.Fatalf("ファイルが見つからなかったんだと思います: %v", pathErr)
}
log.Fatalf("良く分からないエラーが発生しました: %v", pathErr)
}
f.Close()
}
ラベル付けしないからこそ、エラーは詳細に取れる様に設計されています。別のドキュメントにエラーハンドリングについて詳細に書かれています。Error handling and Go - The Go Blog
If you have written any Go code you have probably encountered the built-in error type...
http://blog.golang.org/error-handling-and-go