wouldn't this prevent the goroutine leak, w/o the need to drain the channel?
func iterator(m map[KT]VT) iter { itr := iter{C: make(chan VT), quit: make(chan struct{})} go func() { for _, v := range m { select { case <-itr.quit: return case itr.C <- v: } } close(i) }() return i } func (itr *iter) Close() { close(itr.quit) } that won't address the performance issue, of course. that said, perhaps you could turn it around and have the iteration process take a closure, pretty much like path/filepath.Walk ? -s On Fri, Aug 3, 2018 at 12:29 AM Dan Kortschak <dan.kortsc...@adelaide.edu.au> wrote: > Go lacks a nice way to hand around a map iteration state. There are > ways to to this, but they either require handing around a doubly- > wrapped closure > > ``` > func iterator(m map[KT]VT) func(func(v VT)) { > return func(fn func (v VT)) { > for _, v := range m { > fn(v) > } > } > } > ``` > > which does not allow real pausable iteration without additional > callback horror making is essentially useless for public API (where > this is actually useful). > > A goroutine based system > > ``` > type iter <-chan VT > > func (i iter) next() (VT, bool) { > v, ok := <-i > return v, ok > } > > func iterator(m map[KT]VT) iter { > i := make(chan VT) > go func() { > for _, v := range m { > i <- v > } > close(i) > }() > return i > } > ``` > > which requires that the user must drain to complete map or cause a > goroutine leak and has significant performance impacts still. > > Or to copy out all the keys and then iterate over them > > ``` > type iter struct { > m map[KT]VT > idx int > keys []KT > } > > func (i *iter) next() (VT, bool) { > if i.idx < len(i.keys) { > > v, ok := i.m[i.keys[i.idx]] > i.idx++ > return v, ok > } > var v VT > return v, false > } > > func iterator(m map[KT]VT) *iter { > i := iter{m: m, keys: make([]KT, 0, len(m))} > for k := range m { > i.keys = append(i.keys, k) > } > return &i > } > ``` > > Which may result in significant additional work and allocation and is > fundamentally not lazy. > > > However, there is an iterator state type in runtime that is made use of in > reflect in the Value.MapKeys method. At the moment this is iterated over in > a single pass allocating a []reflect.Value that is intended to be used in a > call to Value.MapIndex in a manner analogous to the last example above. > > Is there a good reason not to provide a map iteration type in reflect to > allow users to avoid that eager work. This would be a closer (though not > identical as is always the case with reflect) approximation to the > language's actual range operator. > > For example (signatures only) > > ``` > type MapRange struct {... > > func (r *MapRange) Next() bool > > func (r *MapRange) Value() Value > > func (v Value) MapRange() *MapRange > ``` > > thanks > Dan > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.