2014/04/16

Recent entries from same category

  1. Go 言語プログラミングエッセンスという本を書きました。
  2. errors.Join が入った。
  3. unsafe.StringData、unsafe.String、unsafe.SliceData が入った。
  4. Re: Go言語で画像ファイルか確認してみる
  5. net/url に JoinPath が入った。

FAQ に書いてあります。
Why does Go not have exceptions? - Frequently Asked Questions (FAQ) - The Go Programming Language

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
try-catch-finally イディオムはコードを複雑にするし、たかがファイルのオープンに失敗しただけのエラーに過剰なラベル付を強要する。 golang では複数の値を返す事が出来るので、一般的には戻り値の最後にエラーを付けて返す。

と書いてあります。
何度読んでも「例外が使いこなせません」とは読めません。すいません。複雑になるのは良くないという話はそもそもレイヤの違う話だと思いますよ。まぁ 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 でも細かいエラーハンドリングは出来ます。この辺はドキュメントを読むと書いてあります。例えば os.Open ならば
func Open - The Go Programming Language

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
エラーがあった場合には PathError が返ります。 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
Posted at by