golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
122.13k stars 17.46k forks source link

proposal: reflect: Add Type.Fields() iter.Seq[StructField] and Type.Methods() iter.Seq[Method] #66631

Open earthboundkid opened 4 months ago

earthboundkid commented 4 months ago

Proposal Details

reflect.Type.Fields() iter.Seq[StructField] and reflect.Type.Methods() iter.Seq[Method] would be more convenient than having to call i := range myType.NumFields() and myType.Field(i) etc.

You could also add iterators to reflect.Value as well but those are less obviously useful. Probably worth doing just for completeness though.

thediveo commented 4 months ago

could this be a duplicate of #66626?

adonovan commented 4 months ago

could this be a duplicate of https://github.com/golang/go/issues/66626?

No, that issue relates to the go/types package, not reflect.

thediveo commented 4 months ago

ah, overlooked this crucial detail, thank you!

earthboundkid commented 4 months ago

This was in fact inspired by #66626, but yes, it is for reflect, not types.

adonovan commented 4 months ago

Alternatively, the method value could be the iterator as opposed to returning an iterator, like so:

package reflect

type Type interface {
    ...
    // Fields is a go1.23 iterator over all the fields of a struct type (etc).
    //
    // Example: for ftype := range t.Fields { ... }
    Fields(yield func(ftype *Func) bool)
}

func (t *rtype) Fields(yield func(ftype *Func) bool) {
    for i := range t.NumFields() {
        if !yield(t.Field(i)) {
            break
        }
    }
}
// client code
var t reflect.Type = ...
for ftype := range t.Fields { ... }
earthboundkid commented 4 months ago

Maybe. That keeps from introducing an import dependency we might not want, but it goes against the general conventions used elsewhere.

earthboundkid commented 1 month ago

I used this in a project and the signature should be iter.Seq2[int, reflect.StructField] so that you can do rv.Field(i).Set(whatever) if the field matches some criteria. It's very convenient though and worth having.

DeedleFake commented 1 month ago

I'd like an iterator version of VisibleFields(), too. I had a need for that the other day, actually.

earthboundkid commented 1 month ago

Isn't that just a filter on field.IsExported() or is there more to it than that? I did only want the exported fields for my thing, but I also needed a type and tag test for the field, so having VisibleFields() per se wouldn't have saved much coding.

DeedleFake commented 1 month ago

VisibleFields() gets the fields recursively into anonymous struct fields as well as unexported ones.

earthboundkid commented 1 month ago

Got it. There's already a slice version of VisibleFields(), so I'm not sure how much the iterator version saves since you'll presumably cache the slice somehow if performance is a concern. I'm willing to be wrong though. There could be AllVisibleFields and VisibleFields could just call and collect that.