LoginSignup
8
3

More than 3 years have passed since last update.

Go言語はerr != nilでもerrはnilである時がある

Last updated at Posted at 2020-04-28

突然ですが問題です。
以下のコードを実行した時、"This should not be called"の文字列は出力されるでしょうか?

package main

import "fmt"

type MyError struct {
}

// implements error interface
func (MyError) Error() string {
    return "This is MyError"
}

func fn() (int, *MyError) {
    return 42, nil
}

func main() {
    result, err := fn()
    if err != nil {
        fmt.Println("This should not be called")
    }
    fmt.Println(result, err)
}

実行結果

42 <nil>

出てません。fn()はエラーnilを返しているので、if err != nilの中には入りません。
ここまではOKですね。

では次の例ではどうでしょうか。

package main

import "fmt"

type MyError struct {
}

// implements error interface
func (MyError) Error() string {
    return "This is MyError"
}

func fn() (int, *MyError) {
    return 42, nil
}

func main() {
    var err error
    result, err := fn()
    if err != nil {
        fmt.Println("This should not be called")
    }
    fmt.Println(result, err)
}

func main()の次の行に1行追加されているだけですが、これはどうなるでしょう?

実行結果

This should not be called
42 <nil>

なんと!"This should not be called"の文字列が出力されてしまいました!
ということはerr != niltrueということになります。
でもちょっとまってください。その下の出力内容は42 <nil>です。ということはerrはやっぱりnilなのです。だから、

if err != nil {
  fmt.Printf("Error %s", err.Error())
}

のようなことをしていると、直前でnilチェックをしているにも関わらずnil参照エラーでクラッシュします。

私はこの現象に数時間悩まされました。

この例で言うところのfn()が返却するエラーの型を*MyErrorではなくerrorとすると、この問題は発生しません。

package main

import "fmt"

type MyError struct {
}

// implements error interface
func (MyError) Error() string {
    return "This is MyError"
}

func fn() (int, error) { // *MyErrorではなくerror
    return 42, nil
}

func main() {
    var err error
    result, err := fn()
    if err != nil {
        fmt.Println("This should not be called")
    }
    fmt.Println(result, err)
}

実行結果

42 <nil>

Goの型システムの深いところまでまだ理解できていないのですが、とりあえず各関数が返却するエラーの型を*MyErrorのような独自型ではなく、errorとするのが良さそうです。

同じハマり方をされていた先人たち

8
3
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3