LoginSignup
3
3

More than 5 years have passed since last update.

bufioのエラーハンドリングをチラ見。

Posted at

Rob Pike先生のErrors are values - The Go Blogを読んだら下記のようなコードが書いてありまして、

scanner := bufio.NewScanner(input)
for scanner.Scan() {
    token := scanner.Text()
    // process token
}
if err := scanner.Err(); err != nil {
    // process the error
}

上の記事を読めばだいたい想像がつくのですが、実際どんな実装なのかと。
まず、Scanner構造体。

bufio/scan.go
type Scanner struct {
    r            io.Reader // The reader provided by the client.
    split        SplitFunc // The function to split the tokens.
    maxTokenSize int       // Maximum size of a token; modified by tests.
    token        []byte    // Last token returned by split.
    buf          []byte    // Buffer used as argument to split.
    start        int       // First non-processed byte in buf.
    end          int       // End of data in buf.
    err          error     // Sticky error.
}

Err()で最初のエラーを取得できる。

bufio/scan.go
// Err returns the first non-EOF error that was encountered by the Scanner.
func (s *Scanner) Err() error {
    if s.err == io.EOF {
        return nil
    }
    return s.err
}

setError(error)は下記。

bufio/scan.go
// setErr records the first error encountered.
func (s *Scanner) setErr(err error) {
    if s.err == nil || s.err == io.EOF {
        s.err = err
    }
}

で、Scan()の中でs.setErr(error)でエラーをセット。

bufio/scan.go
func (s *Scanner) Scan() bool {
    // Loop until we have a token.
    for {
        // See if we can get a token with what we already have.
        if s.end > s.start {
            advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil)
            if err != nil {
                s.setErr(err)
                return false
            }
//中略
        // Is the buffer full? If so, resize.
        if s.end == len(s.buf) {
            if len(s.buf) >= s.maxTokenSize {
                s.setErr(ErrTooLong)
                return false
            }
//中略
        for loop := 0; ; {
            n, err := s.r.Read(s.buf[s.end:len(s.buf)])
            s.end += n
            if err != nil {
                s.setErr(err)
                break
            }
            if n > 0 {
                break
            }
            loop++
            if loop > maxConsecutiveEmptyReads {
                s.setErr(io.ErrNoProgress)
                break
            }
        }
// 以下略

なるほどなるほど。
使えそうなところがあったら使ってみよう。

3
3
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
3
3