絶対ハマるらしいgolangのnilにハマった件

ハマりましたwwww

func main() {
    var e1 error
    e1 = x()
    if e1 == nil {
        fmt.Printf("e1 == nil: %v\n", e1)
    } else {
        fmt.Printf("e1 != nil: %v\n", e1)
    }

    var e2 *NyanError
    e2 = x()
    if e2 == nil {
        fmt.Printf("e2 == nil: %v\n", e2)
    } else {
        fmt.Printf("e2 != nil: %v\n", e2)
    }
}

func x() *NyanError {
    return nil
}

// エラー
type NyanError struct {
}

func (e *NyanError) Error() string {
    return "にゃー"
}

出力

e1 != nil: にゃー
e2 == nil: にゃー

どゆこと?

https://play.golang.org/p/z5pdAdI0YZ

正直go書き始めて3日程度の俺にはよくわからんのだが、errorのようなinterface型の変数は、実行時にnilが渡されたとしても渡されるであろう構造体の型情報を保持しているらしい。んで、この型情報も一致しないと == nil にならないらしいマジすか。nilリテラルは値がnilで型もnilだが、e1は値がnilで型がNyanError、なので不一致。

値はnilなのに、 fmt.Printf(“%v”, e1) で “にゃー” とか表示されるのは変数が型情報を持っているからなんすね…

interface型変数の場合 e == nil || reflect.ValueOf(e).IsNil() と判定するといいらしい。これは、変数の型情報を無くして値だけで比較する方法って事ですか。

しかし、変数の型が構造体型かinterface型かで動き変わるなんて思わんかったよw

scannerで読み込んだ行を並列でjson.Unmershalすると壊れる

golang歴4時間です。

1行1JSONなファイルを処理するため、以下のようなコードをかいたところjson.Unmarshalでエラーになりました。

func main() {
    scanner := bufio.NewScanner(os.Stdin)

    ch := make(chan bool, 3)

    var wg sync.WaitGroup
    for scanner.Scan() {
        line := scanner.Bytes()

        wg.Add(1)
        ch <- true
        go func(line []byte) {
            defer func() {
                wg.Done()
                <-ch
            }()

            var hoge Hoge
            jsonErr := json.Unmarshal(line, &hoge)
            if jsonErr != nil {
                log.Print(jsonErr)
            }
        }(line)
    }
    wg.Wait()
}

goroutineを使わなければ問題ないんですよね。scanner.Bytesで取得した[]byteの値はスレッド間で共有されてjson解析中に書き換わってしまうのでしょうか…。

   for scanner.Scan() {
        b := scanner.Bytes()
        line := make([]byte, len(b))
        copy(line, b)

取得した値をコピーしてやれば大丈夫でした。