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 <[email protected]>
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 [email protected].
> 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 [email protected].
For more options, visit https://groups.google.com/d/optout.