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.

Reply via email to