13
\$\begingroup\$

I need to extract some data from the environment variables using Go. This data can be string, boolean or integer, so I ended up writing three functions.

package main
import (
 "fmt"
 "os"
 "strconv"
)
func getStrEnv(key string) string {
 val := os.Getenv(key)
 if val == "" {
 panic(fmt.Sprintf("some error msg"))
 }
 return val
}
func getIntEnv(key string) int {
 val := getStrEnv(key)
 ret, err := strconv.Atoi(val)
 if err != nil {
 panic(fmt.Sprintf("some error"))
 }
 return ret
}
func getBoolEnv(key string) bool {
 val := getStrEnv(key)
 ret, err := strconv.ParseBool(val)
 if err != nil {
 panic(fmt.Sprintf("some error"))
 }
 return ret
}

Which works, but in a language like Python, I would just create one function getEnv(key, type_var), which would go through various passes depending on the type_var provided. Is there a way to produce similar result with Go?

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Oct 23, 2015 at 23:01
\$\endgroup\$
1
  • \$\begingroup\$ Not a code review, but I wrote github.com/walles/env to solve this very problem using generics. Check it out! \$\endgroup\$ Commented Mar 11, 2023 at 18:06

2 Answers 2

12
\$\begingroup\$

Go is statically typed. Every variable has a static type, that is, exactly one type known and fixed at compile time. You are not in dynamically typed Python anymore.

You could use reflection, BUT DON'T!

Common difficulties with reflection

If people are new to Go, they shouldn't be using reflection at all.

-rob

Your functions are specializations of os.Getenv. Revise the function names to getenvStr, getenvInt, and getenvBool.

os.Getenv retrieves the value of the environment variable named by the key. It returns the value, which will be empty if the variable is not present.

There is often nothing wrong when an environment variable is empty. Instead of panicing, return a named error.

For example,

package main
import (
 "errors"
 "os"
 "strconv"
)
var ErrEnvVarEmpty = errors.New("getenv: environment variable empty")
func getenvStr(key string) (string, error) {
 v := os.Getenv(key)
 if v == "" {
 return v, ErrEnvVarEmpty
 }
 return v, nil
}
func getenvInt(key string) (int, error) {
 s, err := getenvStr(key)
 if err != nil {
 return 0, err
 }
 v, err := strconv.Atoi(s)
 if err != nil {
 return 0, err
 }
 return v, nil
}
func getenvBool(key string) (bool, error) {
 s, err := getenvStr(key)
 if err != nil {
 return false, err
 }
 v, err := strconv.ParseBool(s)
 if err != nil {
 return false, err
 }
 return v, nil
}
func main() {}

References:

Package reflect

The Laws of Reflection

answered Oct 25, 2015 at 14:50
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Since Go 1.18 I think generics is the right tool for this, see my answer about github.com/walles/env. \$\endgroup\$ Commented Mar 11, 2023 at 6:41
0
\$\begingroup\$

You can also abstract an interface, so you can implement other ConfigSources later (e.g. file based).

package main
import (
 "strconv"
 "os"
)
type ConfigSource interface {
 GetString(name string) string
}
type Config struct {
 ConfigSource
}
func (c *Config) GetString(name string) string {
 if nil == c.ConfigSource {
 return ""
 }
 return c.ConfigSource.GetString(name)
}
func (c *Config) GetBool(name string) bool {
 s := c.GetString(name)
 i, err := strconv.ParseBool(s)
 if nil != err {
 return false
 }
 return i
}
func (c *Config) GetInt(name string) int {
 s := c.GetString(name)
 i, err := strconv.ParseInt(s, 10, 0)
 if nil != err {
 return 0
 }
 return i
}
func (c *Config) GetFloat(name string) float64 {
 s := c.GetString(name)
 i, err := strconv.ParseFloat(s, 64)
 if nil != err {
 return 0
 }
 return i
}
type EnvGetter struct {}
func (r *EnvGetter) GetString(name string) string {
 return os.Getenv(name)
}
func main() {
 c := &Config{ConfigSource:EnvGetter{}}
 println("HOME:", c.GetString("HOME"))
 println("SHLVL:", c.GetInt("SHLVL"))
}
answered Oct 26, 2015 at 16:29
\$\endgroup\$
1
  • \$\begingroup\$ Your code has compile errors. \$\endgroup\$ Commented Oct 26, 2015 at 20:37

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.