-
Notifications
You must be signed in to change notification settings - Fork 385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unnecessary initialization of struct pointers #113
Comments
@na-- wondering if you ended up using a workaround for this? |
Unfortunately not, but I haven't had the time to investigate this very thoroughly yet... And there's a good chance I won't get to it anyway, since we may have to switch libraries due to some of envconfig's other "features" |
I think I have a general handle on the issue, and it's a pretty significant structural one. envconfig first makes a recursive pass gathering information about types, and returning deep pointers to the fields to populate. This makes the processing phase relatively simple, but requires that those fields all exist in the first place. That's why initializing all these pointers becomes necessary. I managed to get your example to build the right result, but for the obvious reason this broke the basic functionality of populating nested fields. It'll be a significant refactor to get this right, but I don't have the time to do it. The first part of the puzzle is to not perform any writes to the spec in diff --git a/envconfig.go b/envconfig.go
index 3f16108..2eb549d 100644
--- a/envconfig.go
+++ b/envconfig.go
@@ -85,10 +85,11 @@ func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) {
// nil pointer to a non-struct: leave it alone
break
}
- // nil pointer to struct: create a zero instance
- f.Set(reflect.New(f.Type().Elem()))
+ // nil pointer to struct: use a new zero instance
+ f = reflect.New(f.Type().Elem())
+ } else {
+ f = f.Elem()
}
- f = f.Elem()
}
// Capture information about the config variable |
@teepark well, I tried you approatch, but unsuccessfully. Parser should be rewritten with this thought in mind. Workaround solution is to use the new Go 1.13 function --- envconfig.go (revision 0b417c4ec4a8a82eecc22a1459a504aa55163d61)
+++ envconfig.go (date 1572281234355)
@@ -224,9 +224,47 @@
}
}
+ err = removeEmptyStructs(spec)
+ if err != nil {
+ return err
+ }
+
return err
}
+func removeEmptyStructs(spec interface{}) error {
+ s := reflect.ValueOf(spec)
+
+ if s.Kind() != reflect.Ptr {
+ return ErrInvalidSpecification
+ }
+ s = s.Elem()
+ if s.Kind() != reflect.Struct {
+ return ErrInvalidSpecification
+ }
+
+ for i := 0; i < s.NumField(); i++ {
+ f := s.Field(i)
+
+ if f.Kind() == reflect.Ptr {
+ if !f.IsNil() {
+ if f.Elem().IsZero() {
+ f.Set(reflect.Zero(f.Type()))
+ }
+ }
+
+ if f.Elem().Kind() == reflect.Struct {
+ err := removeEmptyStructs(f.Elem().Addr().Interface())
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
// MustProcess is the same as Process but panics if an error occurs
func MustProcess(prefix string, spec interface{}) {
if err := Process(prefix, spec); err != nil { This function recursively sets empty structs to nil. |
@teepark @L11r Here is the correct version of the removeEmptyStructs that support multiple levels of structs nesting when some levels can be pointers and some not: https://github.com/hexdigest/envconfig/blob/master/envconfig.go#L230:L263 I also added tests for this func. @kelseyhightower not quite sure that ☝️ thing can be a good candidate for a PR because it's more of a cleanup rather than an actual fix for the env parser even though I don't see how it can break something. |
@hexdigest I think you should still submit a PR! Your change fixed the problem |
@kevinpark1217 here it is #201 |
If I have the following code and haven't actually defined any of the environment variables:
I'd expect to get this:
but I actually get:
For some reason,
envconfig
has created an emptySomeItem
and assigned its address toconf.ItemPointer
.The text was updated successfully, but these errors were encountered: