轮询功能的惯用方式,直到确定!= true

I have a function which returns values when polled, but then at some point will stop returning sensible values as shown below.

Is there a more idiomatic way to poll it than checking if !ok every time. I'm thinking something akin to polling a channel with range.

package main

import "fmt"

func iter() func() (int, bool) {
    i := 0
        return func() (int, bool) {
        if i < 10 {
            i++
            return i, true
        }
        return i, false
    }
}

func main() {
    f := iter()
    for {
        v, ok := f()
        if !ok {
            break
        }
        fmt.Println(v)
    }
}

I don't think there's a way to avoid checking ok, but you can restructure it to avoid the ugly break:

for v,ok := f(); ok; v,ok = f() {
    fmt.Println(v)
}

It should be noted that this only works in cases where either:

  1. You have a single function with multiple return values to check, OR

  2. You have one or more functions with only one return value to check

Unfortunately Go won't let you do things like

f := iter()
g := iter()
v,ok,v2,ok2 := f(), g(); ok && ok2; v,ok,v2,ok2 := f(), g() {
   // code
}

So if you have a case with multiple functions you're stuck with ifs and breaks unless they only return a single value.

That said, (and on reflection), the more idiomatic way to write an iterator in Go is ranging over a channel. Consider the equivalent program:

func Iterator(iterCh chan<- int) {
    for i := 0; i < 10; i++ {
       iterCh <- i
    }
    close(iterCh)
}

func main() {
    iter := make(chan int)
    go Iterator(iter)
    for v := range iter {
       fmt.Println(v)
    }
}

In this case, instead of returning a boolean value, just close the channel whenever you're done sending values. The downside of this method is that if you want to return multiple values, you have to make a struct of some sort to send over the channel.

And finally, if you want to wrap it a bit to hide the channel boilerplate every time you run your iterator:

func Iter() <-chan int {
   iterChan := make(chan int)
   go iter(iterChan)
   return iterChan
}
func iter(iterCh chan<- int) {
    for i := 0; i < 10; i++ {
       iterCh <- i
    }
    close(iterCh)
}

func main() {
    for v := range Iter() {
       fmt.Println(v)
    }
}

Which is more code for the initial implementation, but removes having to manually declare a channel every time you want to use an iterator.

I don't see how your example is much different from the common idiom for reading until the end of file. For example,

package main

import (
    "bytes"
    "fmt"
    "io"
    "strings"
)

func main() {
    buf := bytes.NewBufferString("line1
line2")
    for {
        line, err := buf.ReadString('
')
        if err != nil {
            if err != io.EOF {
                fmt.Println(err)
                return
            }
            if len(line) == 0 {
                break
            }
        }
        line = strings.TrimSuffix(line, "
")
        fmt.Println(line)
    }
}

Output:

line1
line2

Your example looks idiomatic to me.