Currently, the only way to dynamically access the fields in a struct is through reflection. Having a simple way to range over structs as if they were map[string]anys would enable various struct idioms to be used more often.
See below for some examples of such idioms:
type vars struct {
Editor string
Shell string
Pid int
}
func ExampleGetFields() {
env := vars{
Editor: "vim",
Shell: "/bin/dash",
Pid: 100,
}
for name, val := range structs.Fields(env) {
name := strings.ToUpper(name)
val := fmt.Sprint(val) // val is a string/int because env was passed by value
os.Setenv(name, val)
}
fmt.Println(os.Environ())
//Output:
// [... EDITOR=vim SHELL=/bin/dash PID=100 ...]
}
func ExampleSetFields() {
var env vars
for name, val := range structs.Fields(&env) {
name := strings.ToUpper(name)
switch val := val.(type) { // val is a *string/*int because &env was passed
case *string:
*val = os.Getenv(name)
case *int:
*val, _ = strconv.Atoi(os.Getenv(name))
}
}
fmt.Printf("%#v\n", env)
//Output:
// {Editor:"nano", Shell:"/bin/bash", Pid:2499}
}
func ExampleStringKeys() {
var env vars
{
envMap := maps.Collect(structs.Fields(&env))
*envMap["Editor"].(*string) = "code"
*envMap["Shell"].(*string) = "/bin/zsh"
pidKey := "P" + strings.ToLower("ID")
*envMap[pidKey].(*int) = 42
}
fmt.Printf("%#v\n", env)
//Output:
// {Editor:"code", Shell:"/bin/zsh", Pid:42}
}
Sample implementation (may contain errors):
package structs
func Fields(strukt any) iter.Seq2[string, any] {
return func(yield func(string, any) bool) {
strukt := reflect.ValueOf(strukt)
isPointer := strukt.Kind() == reflect.Pointer
if isPointer {
if strukt.IsNil() {
return
}
strukt = strukt.Elem()
}
for i := range strukt.NumField() {
field := strukt.Type().Field(i)
if !field.IsExported() {
continue
}
fieldName := field.Name
fieldVal := strukt.Field(i)
if isPointer {
fieldVal = fieldVal.Addr()
}
if !yield(fieldName, fieldVal.Interface()) {
return
}
}
}
}
Proposal Details
Currently, the only way to dynamically access the fields in a struct is through reflection. Having a simple way to
range
over structs as if they weremap[string]any
s would enable various struct idioms to be used more often.See below for some examples of such idioms:
Sample implementation (may contain errors):