goの3つのiterator
参考元
https://blog.kowalczyk.info/article/1Bkr/3-ways-to-iterate-in-go.html
github
https://github.com/kjk/go-cookbook/tree/master/3-ways-to-iterate
こちらのgolangのiteratorに関する記事が参考になったので、まとめました。
3つの common iteration patternがgoにはあるそうです。
- callbacks
 - an iterator object with Next() method
 - channels
 
callback
func iterateOddNums(max int, f func(n int) error) error {
 if max < 0 {
     return errors.New("max must be greater than 0")
 }
 for i := 1; i <= max; i += 2 {
     err := f(i)
     if err != nil {
         return err
     }
 }
 return nil
}
func printOddNumbers(max int) error {
 err := iterateOddNums(max, func(n int) error {
     fmt.Printf("%d\n", n)
     return nil
 })
 return err
}
func main() {
 if err := printOddNumbers(20); err != nil {
     log.Fatal(err)
 }
}
関数を引数でもらうpattern。
callback関数はiterateを中断するべきかどうかの情報を返す
filepath.Walk で使われているpatternとのこと
next
type OddNumIterator struct {
 max     int
 current int
 err     error
}
func NewOddNumIterator(max int) *OddNumIterator {
 var err error
 if max < 0 {
     err = errors.New("max must be greater than 0")
 }
 return &OddNumIterator{
     max:     max,
     current: 1,
     err:     err,
 }
}
func (i *OddNumIterator) Next() bool {
 if i.err != nil {
     return false
 }
 i.current += 2
 return i.current <= i.max
}
func (i *OddNumIterator) Value() int {
 if i.err != nil || i.current > i.max {
     panic("something wrong")
 }
 return i.current
}
func (i *OddNumIterator) Err() error {
 return i.err
}
func printOddNums(max int) error {
 iter := NewOddNumIterator(max)
 for iter.Next() {
     fmt.Printf("%d\n", iter.Value())
 }
 return iter.Err()
}
func main() {
 if err := printOddNums(20); err != nil {
     log.Fatal(err)
 }
}
- 
database/sqlのRows.Next - 
bufioのScanner.Scan 
等で利用されていると思います
goroutine safeにするには, sync.Mutex を埋め込むべきか。
Value() がintのみを返すために、中でpanicを呼ぶのも直したい
channel
type IntWithErr struct {
 Int int
 Err error
}
func generateOddNums(ctx context.Context, max int) <-chan IntWithErr {
 ch := make(chan IntWithErr)
 go func() {
     defer close(ch)
     if max < 0 {
         ch <- IntWithErr{
             Err: errors.New("max must be greater than 0"),
         }
         return
     }
     for i := 1; i <= max; i += 2 {
         if ctx != nil {
             select {
             case <-ctx.Done():
                 return
             default:
             }
         }
         ch <- IntWithErr{
             Int: i,
         }
     }
 }()
 return ch
}
func printOddNums(max int, stopAt int) error {
 ctx, cancel := context.WithCancel(context.Background())
 defer cancel()
 ch := generateOddNums(ctx, 20)
 for val := range ch {
     if val.Err != nil {
         return val.Err
     }
     if val.Int > stopAt {
         cancel()
         break
     }
     fmt.Printf("%d\n", val.Int)
 }
 // drain
 for _ = range ch {
 }
 return nil
}
func main() {
 if err := printOddNums(100, 20); err != nil {
     log.Fatal(err)
 }
}
channelを利用するとpythonのgeneratorのように利用できるのだろうか。
cancel()を読んでも、呼んだ先が処理を停止する保証がないので、channelからdrainしないとgoroutine leak(?) するとのこと。
go func() {
   for _ = range ch {
   }
}()
のようにdrain処理も別のgoroutineに切り出したほうが安全かなと思った。
どれがBestか
The one that best fits your scenario.
やっぱりそうですよね.