Go Global Variables and Scopes of slices - go

I am starting out with GO language, and getting an error I cannot figure out. How do I create a global slice that all functions within the module can use? Here is what I have:
package main
import (
"fmt"
)
type Req struct {
Req int
Name string
}
var Reqs []Req
func ReadReqs(fp string) {
var CReq Req;
CReq.Req = 1
CReq.Name = "first"
Reqs := append(Reqs, CReq)
}
func main() {
Reqs := make([]Req, 0)
if len(Reqs) > 0 {
fmt.Println(Reqs[0])
}
fmt.Println(Reqs)
}
This code will not compile because of the following error:
./question.go:18: Reqs declared and not used
I was thinking that declaring var Reqs []Req should declare the variable, but it does not seem to be aware of it inside the ReadReqs function. I do realize that globals are BAD but I would like to use global var for this simple program.

Okay first of all I'd recommend you to read Effective Go before continuing.
You are declaring your global variable using:
var Reqs []Req
Then re-declaring a variable with the same name using:
Reqs := ......
You are declaring two different variables.
var Name type also initializes the variable:
var s string
Is equivalent to:
s := ""
So this makes the following line useless:
Reqs = make([]Req, 0)
You can try your fixed code here (Golang Play).

With := you are declaring a new variable (not writing to the global) and that new variable at function scope is unused. (Has nothing to do with globals.)

You're re-declaring Reqs with the := operator. Drop the colon.
You should probably start with the basics first:
Tour of Go
How to Write Go Code
Effective Go

Related

Short declaration in Go

I'm learning Go and I don't understand how the code is allowing me to redeclare the same variable "phones". I thought you can only short-declare a variable once inside a function scope and then you can redeclare that variable only when you are declaring a new variable with it. But with the code below, I'm able to short-declare "phones" twice without declaring a new variable in the second short-declare statement.
package main
import "fmt"
func main() {
phones := map[string]string{
"bowen": "202-555-0179",
"dulin": "03.37.77.63.06",
"greco": "03489940240",
}
multiPhones := map[string][]string{
"bowen": {"202-555-0179"},
"dulin": {"03.37.77.63.06", "03.37.70.50.05", "02.20.40.10.04"},
"greco": {"03489940240", "03489900120"},
}
fmt.Println(phones)
who, phone := "greco", "N/A"
if phones := multiPhones[who]; len(phones) >= 2 {
fmt.Println(phones)
phone = phones[1]
}
fmt.Printf("%s's 2nd phone number: %s\n", who, phone)
}
I thought you can only short-declare a variable once inside a function scope
No, this is wrong. Go is block scoped, not function scoped.
Each variable can be declared once per block and if starts a new block.
(Note that this holds for any type of declaration, be it short or long.)
Go does not allow redefined variables in the same scope.
The code you have contains variables in two different scopes.
This is allowed in Go. It is not a problem.
Your code is similar to:
func main() {
name := "adam"
if name := true; name != false { // the var name here is in if scope.
fmt.Println("name in if scope is :", name)
}
fmt.Println("name out if scope is : ", name)
// name := "jawad" this is error. redifined in same scope not allowed.
}

Calling a function with Go Reflect

I was wondering if it was possible to not know a function name but call it anyway and get values from it. This lead me to the reflection package and I got pretty close but I’m not sure about the last step - if there is one. Again please forgive me if I am missing something obvious, this is my first attempt at doing anything in Go other than getting it setup.
Of course being a compiled language there is no need to iterate through things to find function names, I know them all, but this is something I want to see if it is possible… I’m playing and learning.
Below is the code. What I would really like to do is in the main line extract the values set in ModuleBoot() <“1.0012”, 23> and SomethingBoot() <“1.0000”, 10> but so far all as I can get is structure information. Perhaps that’s just the way it is but perhaps there is a step or change that can make it go the next step.
Hopefully I copied all the relevant code over correctly so it compiles as is:
// Using: go version go1.9.7 linux/amd64
=======================================
FILE: main.go
=======================================
package main
import (
"fmt"
"reflect"
"playing/modules/core"
)
func main() {
miType := reflect.TypeOf(core.ModuleInfo{})
fmt.Println("")
for i := 0; i < miType.NumMethod(); i++ {
method := miType.Method(i)
fmt.Println(method.Name)
in := make([]reflect.Value, method.Type.NumIn())
in[0] = reflect.ValueOf(core.ModuleInfo{})
//fmt.Println("Params in:", method.Type.NumIn(), "Params out:", method.Type.NumOut())
mi := method.Func.Call(in)
fmt.Println("mi:", mi)
fmt.Println("")
}
}
=======================================
FILE: playing/modules/core/something.go
=======================================
package core
func (mi ModuleInfo) SomethingBoot() ModuleInfo {
mi.Version = "1.0000"
mi.Priority = 10
return mi
}
=======================================
FILE: playing/modules/core/modules.go
=======================================
package core
type ModuleInfo struct {
Version string
Priority int
}
func (mi ModuleInfo) ModuleBoot() ModuleInfo {
mi.Version = "1.0012"
mi.Priority = 23
return mi
}
The output I got from this was:
Started delve with config "Debug"
SomethingBoot
mi: [<core.ModuleInfo Value>]
ModuleBoot
mi: [<core.ModuleInfo Value>]
delve closed with code 0
To get the return value as a ModuleInfo, get the underlying value of the first return value and type assert that interface value to ModuleInfo:
// mi has type core.ModuleInfo
mi := method.Func.Call(in)[0].Interface().(core.ModuleInfo)
Run it on the Playground.
You can cut some of the reflect code by type asserting the method to a function with the correct signature and calling that function directly:
for i := 0; i < miType.NumMethod(); i++ {
method := miType.Method(i).Func.Interface().(func(core.ModuleInfo) core.ModuleInfo)
mi := method(core.ModuleInfo{})
fmt.Println("Version", mi.Version)
fmt.Println("Priority", mi.Priority)
fmt.Println("")
}
Run it on the Playground
Go natively supports functions as values; you don't need reflection to do this.
In particular, if you make your two functions top-level functions (not specifically tied to a struct):
package core
type ModuleInfo struct { ... }
func SomethingBoot() ModuleInfo
func ModuleBoot() ModuleInfo
Then you can write a function that takes a function as a parameter:
func PrintVersion(func booter() core.ModuleInfo) {
mi := booter()
fmt.Printf("version %s\n", mi.Version)
}
And you can just pass the pre-existing functions as parameters:
PrintVersion(core.SomethingBoot)
PrintVersion(core.ModuleBoot)
Notice that there aren't parentheses after the function name: you are passing the function itself as a parameter, not calling the function and passing its return value.

How to assign default value if env var is empty?

How do you assign a default value if an environment variable isn't set in Go?
In Python I could do mongo_password = os.getenv('MONGO_PASS', 'pass') where pass is the default value if MONGO_PASS env var isn't set.
I tried an if statement based on os.Getenv being empty, but that doesn't seem to work due to the scope of variable assignment within an if statement. And I'm checking for multiple env var's, so I can't act on this information within the if statement.
There's no built-in to fall back to a default value,
so you have to do a good old-fashioned if-else.
But you can always create a helper function to make that easier:
func getenv(key, fallback string) string {
value := os.Getenv(key)
if len(value) == 0 {
return fallback
}
return value
}
Note that as #michael-hausenblas pointed out in a comment,
keep in mind that if the value of the environment variable is really empty, you will get the fallback value instead.
Even better as #ŁukaszWojciechowski pointed out, using os.LookupEnv:
func getEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}
What you're looking for is os.LookupEnv combined with an if statement.
Here is janos's answer updated to use LookupEnv:
func getEnv(key, fallback string) string {
value, exists := os.LookupEnv(key)
if !exists {
value = fallback
}
return value
}
Go doesn't have the exact same functionality as Python here; the most idiomatic way to do it though, I can think of, is:
mongo_password := "pass"
if mp := os.Getenv("MONGO_PASS"); mp != "" {
mongo_password = mp
}
To have a clean code I do this:
myVar := getEnv("MONGO_PASS", "default-pass")
I defined a function that is used in the whole app
// getEnv get key environment variable if exist otherwise return defalutValue
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if len(value) == 0 {
return defaultValue
}
return value
}
Had the same question as the OP and found someone encapsulated the answers from this thread into a nifty library that is fairly simple to use, hope this help others!
https://github.com/caarlos0/env
For more complex application you can use tooling such as viper, which allows you to set global custom default values, parse configuration files, set a prefix for your app's env var keys (to ensure consistency and name spacing of env var configurations) and many other cool features.
Sample code:
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.AutomaticEnv() // read value ENV variable
// Set default value
viper.SetEnvPrefix("app")
viper.SetDefault("linetoken", "DefaultLineTokenValue")
// Declare var
linetoken := viper.GetString("linetoken")
fmt.Println("---------- Example ----------")
fmt.Println("linetoken :", linetoken)
}
I also had the same problem and I just created a small package called getenvs exactly to answer this problem.
Getenvs supports string, bool, int and float and it can be used like below:
package main
import (
"fmt"
"gitlab.com/avarf/getenvs"
)
func main() {
value := getenvs.GetEnvString("STRING_GETENV", "default-string-value")
bvalue, _ := getenvs.GetEnvBool("BOOL_GETENV", false)
ivalue, _ := getenvs.GetEnvInt("INT_GETENV", 10)
fmt.Println(value)
fmt.Println(bvalue)
fmt.Println(ivalue)
}
In case you are OK with adding little dependency you can use something like https://github.com/urfave/cli
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "language for the greeting",
EnvVar: "APP_LANG",
},
}
app.Run(os.Args)
}

Is it possible to declare a map at package level in golang?

I want a make global map. I am trying the following
package main
import "fmt"
globalMap := make(map[string]string)
func main() {
globalMap["a"] = "A"
fmt.Println(globalMap)
}
It gives me following compilation error on line globalMap := make(map[string]string):
expected declaration, found 'IDENT' mas
non-declaration statement outside function body
Looking at the error i understand it won't allow me to create a global map. what could the best way to create a global map ?
Thanks.
You can’t use the := syntax outside a function body, but you can use the normal variable declaration syntax:
var globalMap = make(map[string]string)

Constants in Go established during init

In my Go program, there are configuration values that I want to be constant for the duration of program execution, but that I want to be able to change at the deployment site. As far as I can tell, there's no way to achieve this with the const keyword, since (again, as far as I can tell) its value must be a constant specified at compile time. This means that the only way to achieve what I want would be to declare normal variables and initialize them during the package's init function. It's not that that won't work, but rather that there will now be nothing to prevent these pseudo-constant's values from changing.
My two questions are:
Am I missing something about how const works?
Assuming I'm not, what's the preferred way to handle this? A public function that returns a private variable that I never expose, never changing it? Just hoping people don't alter the variables, since they're really configuration settings?
Create a file "config.go" and create the vars you want to expose.
Don't export them (make them all lower case). Instead create public (upper case) funcs that give access to each item.
package config
var x = 10
func X() int {
return x
}
When you want those variables you simply import ./config and use them in code as follows:
if config.X()
Obviously, you can set the variables in the package init.
The following code is almost the same as the second method of #Christopher, except that it is not a module, it located in the main package.
package main
import (
"os"
)
type Config struct {
debug bool
key string
proxyNumber int
}
func (c *Config) Debug() bool {
return c.debug
}
func (c *Config) Key() string {
return c.key
}
func (c *Config) ProxyNumber() int {
return c.proxyNumber
}
const (
CONFIG_NAME = "config.ini"
)
var config *Config
func init() {
DefaultConfig()
if Exists(CONFIG_NAME) {
//try to save the config file
}else {
//try to load from the config file
}
}
func DefaultConfig() {
config = &Config{debug:true, key:"abcde",
proxyNumber:5,
}
}
//Exist: check the file exist
func Exists(path string) bool {
_, err := os.Stat(path)
if err == nil { return true }
if os.IsNotExist(err) { return false }
return false
}
you can use config to load and save the config file.
This is a very good question because it delves into what I suspect may be an omission from Go - immutable state.
From the language reference, "constant expressions may contain only constant operands and are evaluated at compile-time."
You cannot make vars constant, which is a shame. Joe's answer proposes encapsulation as a solution, which will work well - but it's verbose, tedious and might introduce errors.
By comparison, many impure functional languages combine mutable variables with single-assignment immutable values. For example, Scala has the keywords 'val' and 'var'; the meaning of Scala's 'var' is quite similar to Go's 'var'. Immutability is a useful tool in the toolbox because referentially-transparent side-effect-free functions can be written, alongside stateful mutable procedural code. Both have their place. Immutability is also an valuable tool for concurrency because there is no worry about possible race conditions if immutable values are shared between goroutines.
So in my opinion, amongst its many strengths, this is one of Go's shortcomings. It would presumably not be hard to support vals as well as vars, the difference being that the compiler checks that each val is assigned exactly once.
Until that feature is added, you have encapsulation as your only option.
You can do something like this:
package main
import (
"fmt"
"strconv"
)
var a string
func main() {
myvar, err := strconv.Atoi(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(myvar)
}
and compile the program with
go build -ldflags '-X main.a 10' test.go
That way you can define a constant during compile time.
Just use standard go flags with iniflags. Standard go flags allow setting arbitrary config variables at program start via passing command-line flags, while iniflags "magically" add support for reading config variables from ini files.
You can wrap the variable in a function that returns its value:
func genConst(x int) func() int {
return func() int {
return x
}
}
var Constx = genConst(5)
var x1 = Constx()
This way the value cannot be changed by accident, even in the file where it's defined

Resources