1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Goでループ内で逐次カーソル動かすやつを書くときのエラー処理

Last updated at Posted at 2025-05-05

Goを使ってイテレータっぽい処理を書くとき、構造体に諸々の情報を詰めて、 Next() で次のデータを読み込むみたいな処理を書くことが多いと思います。その際のエラー報告をどうするのが良いんだろうという内容です。

こうすると良さそう

Next()が返すのは次のレコードがあるかどうかを判別するためのboolのみとし、エラーの報告は別途それ用のフィールドと関数を生やすのが良さそうです。

type Hoge struct {
  ...
  err error
}

...

func (h *Hoge) Next() bool {
  // 次のレコードとかに移動する処理
  // エラーがあったらh.errに入れる
}

func (h *Hoge) Err() error {
  return h.err
}

なぜこれが良さそうか

Goのif文では、;を区切りとすることで条件部分に複数の文を書くことができます:

if data, ok := Something(); ok {
...
}

しかし、for文ではこの書き方はできません:

// うまく認識されない
for data, ok := Something(); ok {
...
}

ということはつまり、データを持ってきたうえでエラーチェックをしてループを回すといった処理ができないということになります。
こういう書き方ができないというわけです:

for hasNext, err := something.Next(); hasNext && err != nil {
...
}

いい感じにfor文の条件式の中で条件を組み立てることができないため、Next()の戻り値にerrを含めると色々面倒な感じになります。
素直にNext()はboolのみを返すことにして、エラーチェックは別で行うことにすればシュッと書けます。

Goのライブラリの実装を見てみる

実際、Goの標準ライブラリでも同じような書き方をされることが多いようです。

bufio.Scanner

それぞれこんな感じのシグネチャになっています。

func (s *Scanner) Scan() bool
func (s *Scanner) Err() error

参考:https://pkg.go.dev/bufio#Scanner

database/sql

sqlでレコードを取り出すやつですね。同じような書き方です。

func (rs *Rows) Next() bool
func (rs *Rows) Err() error

参考:https://pkg.go.dev/database/sql#Rows

ややつらそうな点

ここで書いたものと同じ形でエラー報告するように強制するのが難しそうだなと感じました。
Err()が何を意図して作られているのかは、インタフェース定義にコメントなんかで残すしかありませんし、このあたりの気持ちを察してもらうのは認知コストが大きそうに思えます。
インタフェースのシグネチャだけでいい感じに気持ちが伝わる書き方できないかなぁ…
それとも私がこれまで知らなかっただけで、Gopherの皆様の中ではこの書き方は割と常識なんですかね…

1
0
0

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?