Pointers 反射:设置指针的字段

Pointers 反射:设置指针的字段,pointers,reflection,go,Pointers,Reflection,Go,我正在尝试这样做: 使用名为env的标记定义结构: type Env struct { Port string `env:"PORT"` } 调用一些函数,使用os.Getenv获取环境变量名,并将其放入结构中 func ParseEnv(val interface{}) { ptrRef := reflect.ValueOf(val) if ptrRef.Kind() != reflect.Ptr { panic("pointer to struct

我正在尝试这样做:

使用名为
env
的标记定义结构:

type Env struct {
     Port string `env:"PORT"`
}
调用一些函数,使用
os.Getenv
获取环境变量名,并将其放入结构中

func ParseEnv(val interface{}) {
    ptrRef := reflect.ValueOf(val)
    if ptrRef.Kind() != reflect.Ptr {
        panic("pointer to struct expected")
    }
    ref := ptrRef.Elem()
    if ref.Kind() != reflect.Struct {
        panic("pointer to struct expected")
    }
    refType := ref.Type()
    for i := 0; i < refType.NumField(); i++ {
        field := refType.Field(i)
        value := os.Getenv(field.Tag.Get("env"))
        if value == "" {
            continue
        }
        ref.Field(i).SetString(value)
    }
}
现在,我有这个:

package main

import (
    "fmt"
    "os"
    "reflect"
)

func ParseEnv(t interface{}, v interface{}) {
    it := reflect.TypeOf(t)
    for i := 0; i < it.NumField(); i++ {
        field := it.Field(i)
        value := os.Getenv(field.Tag.Get("env"))
        if value == "" {
            continue
        }
        reflect.ValueOf(v).Elem().FieldByName(field.Name).SetString(value)
    }
}

type Env struct {
    Port        string `env:"PORT"`
    DatabaseURL string `env:"DATABASE_URL"`
}

func main() {
    os.Setenv("PORT", "8080")
    os.Setenv("DATABASE_URL", "postgres://user:pass@host:5432/my-db")
    env := Env{}
    ParseEnv(env, &env)
    fmt.Println(env)
}
主程序包
进口(
“fmt”
“操作系统”
“反映”
)
func ParseEnv(t接口{},v接口{}){
it:=反射.类型(t)
对于i:=0;i

但是,正如您所看到的,我必须将引用和指针传递给我的函数

虽然这样做有效,但它非常丑陋(至少我认为是这样)

如果我试图只传递指针,我就无法正确地获取类型(因为它将是一个
*接口{}
),并且,如果我只传递引用,我就无法使用
reflect
设置值(即使我可以,它也不会工作)

有没有一种明智的方法

下面是一种“更理智”的方式来做你想做的事。您会注意到,我们不需要传入结构的两个副本,而只需要指向结构的指针

func ParseEnv(val interface{}) {
    ptrRef := reflect.ValueOf(val)
    if ptrRef.Kind() != reflect.Ptr {
        panic("pointer to struct expected")
    }
    ref := ptrRef.Elem()
    if ref.Kind() != reflect.Struct {
        panic("pointer to struct expected")
    }
    refType := ref.Type()
    for i := 0; i < refType.NumField(); i++ {
        field := refType.Field(i)
        value := os.Getenv(field.Tag.Get("env"))
        if value == "" {
            continue
        }
        ref.Field(i).SetString(value)
    }
}
示例:

下面是一种“更理智”的方式来做你想做的事情。您会注意到,我们不需要传入结构的两个副本,而只需要指向结构的指针

func ParseEnv(val interface{}) {
    ptrRef := reflect.ValueOf(val)
    if ptrRef.Kind() != reflect.Ptr {
        panic("pointer to struct expected")
    }
    ref := ptrRef.Elem()
    if ref.Kind() != reflect.Struct {
        panic("pointer to struct expected")
    }
    refType := ref.Type()
    for i := 0; i < refType.NumField(); i++ {
        field := refType.Field(i)
        value := os.Getenv(field.Tag.Get("env"))
        if value == "" {
            continue
        }
        ref.Field(i).SetString(value)
    }
}

示例:

哇,我在那里做了一些严重错误的事情。。。非常感谢蒂姆!哇,我在那里做了一些严重错误的事情。。。非常感谢蒂姆!