go global variable not reflecting the correct value - go

I have the following code
package main
import (
"fmt"
"flag"
)
var outputOnly bool
func something() string {
if outputOnly {
fmt.Println("outputting only")
} else {
fmt.Println("executing commands")
}
return "blah"
}
func main() {
vmoutputonlyPtr := flag.Bool("outputonly",false,"If set it will only output the commands it would execute, naturally without the correct parameter values set.")
flag.Parse()
outputOnly := *vmoutputonlyPtr
if outputOnly {
fmt.Println("outputonly commands will not execute")
}
var blah string
blah = something()
fmt.Println("blah is " + blah)
}
But the output is this:
$ ./se -outputonly
outputonly commands will not execute
executing commands
ie. it appears that the function something() is aware of the global variable, but does not reflect its true value. This is my first attempt at golang. What am I doing wrong?

The problem is this line in main.
outputOnly := *vmoutputonlyPtr
:= declares a new variable on the left, outputOnly, of the type of the expression on the right, *vmoutputonlyPtr and assigns the expression to it. It's equivalent to...
var outputOnly bool = *vmoutputonlyPtr
This new outputOnly "shadows" your global outputOnly in its scope. So all the code after outputOnly := *vmoutputonlyPtr in main refers to this outputOnly local to main. While something() refers to the global outputOnly.
See Redeclaration and Reassignment in Effective Go for more about variable shadowing in Go.
If you want to assign to an existing variable, just use =.
outputOnly = *vmoutputonlyPtr

Related

How to make nil interface to struct in golang

I'm new in Golang.
I executed the code below. I get empty humans array in the end.
What should I do in func F?
For testing(monkeypatch) sake. I have to follow the way how the origin func is called.
package main
import (
"fmt"
)
type Human struct {
Name string
}
type Cat struct {
Name string
}
func F(arr interface{}) {
switch arr.(type) {
case *[]*Human:
arr = &[]*Human{{Name: "abc"}}
arr = arr.(*[]*Human)
case *[]*Cat:
arr = &[]*Cat{{Name: "meow"}}
arr = arr.(*[]*Cat)
}
}
func main() {
var humans []*Human
F(&humans)
fmt.Println(humans)
var cats []*Cat
F(&cats)
fmt.Println(cats)
}
The answer, and the main issue cause as well, is that Go always uses pass by value (or copy of the value) when arguments are passed around to function or assigned to variables.
Your function F takes an arr argument:
func F(arr interface{}) {
//...
}
When called from your main function, you are passing an []*Human pointer as an argument, which values will be copied and fed to your function F for execution.
Going back to your function F body, the arr will be having the same value passed by main, which happens to be the address to the original []*Human struct. Upon assigning a new value to arr:
func F(arr interface{}) {
switch arr.(type) {
case *[]*Human:
arr = &[]*Human{{Name: "abc"}}
// ...
}
}
You are assigning a new value to the local arr variable and not to the original pointer, which remains, indeed, unchanged.
To update the value toward which the argument pointer is referring to, you should used the dereferrence symbol:
func F(arr interface{}) {
switch arr := arr.(type) {
case *[]*Human:
*arr = []*Human{&Human{Name: "abc"}}
fmt.Println(arr)
// ...
}
}
Note the switch arr := arr.(type) statement which creates a new arr variable (shadowing the argument arr) with the interface dynamic type to be able to assign the proper value to it.

How do I return a string from a function that is inside a for loop?

I have a function I have written and I would like it to return a string. I have declared the variable before my for loop, but when I try and return it at the end of the function it claims the variable is unused. I do not seem to have the problem when returning other types. I am trying to figure out what I am doing wrong.
func GetInstanceAlarms(instance *Instance) string {
params := &cloudwatch.DescribeAlarmsInput{}
c := cloudwatch.New(session.New())
resp, err := c.DescribeAlarms(params)
var Arn string
if err != nill {
panic(err.Error())
}
for idx := range resp.MetricAlarms{
for _, alarm := range resp.MetricAlarms[idx].Dimensions{
if *alarm.Name == "InstanceId" && *alarm.Value == instance.InstanceId{
log.Println(resp.MetricAlarms[idx].AlarmArn)
//claims this variable is unused
Arn := resp.MetricAlarms[idx].AlarmArn
}
}
}
return Arn
}
Inside your for loop you have recreated a variable Arn which shadows the outer variable. Remember that := creates new variables, and = assigns to existing variables.
Arn := resp.MetricAlarms[idx].AlarmArn
This Arn variable is only valid in its containing block, and once you exit the block, the outer variable Arn, created just before the loop, becomes visible again.
Instead of creating a new variable, assign to the existing one.
Arn = resp.MetricAlarms[idx].AlarmArn
Read more about scope in Go.
Running go vet should catch this issue.
In you code you have err!=nill, change to err != nil
I check your code, I don't see any issue. Once you declared var Arn string, by default it has zero value i.e empty string "".
I tested with (Mac Os) with the following code and I don't see any error. Also I have added in code to print go version and OS.
package main
import (
"fmt"
"runtime"
)
func main(){
fmt.Printf("Checking on %s OS with Go version %s\n",runtime.GOOS, runtime.Version())
fmt.Println(test())
}
func test() string {
var a string
for i:=0;i<5;i++{
a:="string"
return a
}
return a
}

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)
}

Value assigned and not used in if statement

I wrote an example of the problem I'm seeing in go playground:
https://play.golang.org/p/rPCqAC56Ff
It's pretty self evident, but I'm declaring a variable outside of an if statement, setting variable in if and then using outside of if.
The question is simple, why doesn't this work?
package main
import (
"fmt"
"os"
)
func main() {
var foo string
if true {
foo = "foo"
} else {
foo, found := os.LookupEnv("GOPATH")
if !found {
fmt.Printf("who cares.\n")
}
}
println(foo)
}
You're creating a new variable foo in if block with :=
foo, found := os.LookupEnv("GOPATH")
Check blocks and the scoping rules.
The correct code:
package main
import (
"fmt"
"os"
)
func main() {
var foo string
var found bool
if true {
foo = "foo"
} else {
foo, found = os.LookupEnv("GOPATH")
if !found {
fmt.Printf("who cares.\n")
}
}
println(foo)
}
From Go documentation:
An identifier declared in a block may be redeclared in an inner block.
While the identifier of the inner declaration is in scope, it denotes
the entity declared by the inner declaration.
:= in the else block redeclares foo, so that foo now refers to a whole new variable with the same name, a variable which is never used. Even if we somehow reached the else block, the println(foo) at last line wouldn't print our $GOPATH as that's stored to the other foo variable (or was until the whole variable went out of scope)
Correct code would be:
func main() {
var foo string
if true {
foo = "foo"
} else {
var found bool
foo, found = os.LookupEnv("GOPATH")
if !found {
fmt.Printf("who cares.\n")
}
}
println(foo)
}
It's easy to get confused since this code works fine even though foo is redeclared before it used:
func main() {
var foo string
foo = "foo"
foo, found := os.LookupEnv("GOPATH")
if !found {
fmt.Printf("who cares.\n")
}
println(foo)
}
What's happening here is that there's also another, different kind of legal redeclaration in Go:
Unlike regular variable declarations, a short variable declaration
may redeclare variables provided they were originally declared earlier
in the same block (or the parameter lists if the block is the function
body) with the same type, and at least one of the non-blank variables
is new. As a consequence, redeclaration can only appear in a
multi-variable short declaration. Redeclaration does not introduce a
new variable; it just assigns a new value to the original.
So in this other kind of redeclaration, no new foo variable is actually created and it works just like assignment to foo.

Why the shorthand syntax to declare/init variables?

Is there a difference between these 2 styles of variable declaration/initialization?
package main
import "fmt"
func main() {
var a = "I am a string" // Declare + init (infer)
fmt.Println(a)
b := "I am a string" // Declare + init (shorthand)
fmt.Println(b)
}
I fail to see the added value of the shorthand syntax, and inclined to use the "var" statement for consistency throughout my code.
I always try to use the := syntax. The benefit is huge when you need to Refactor code.
You are not binding the name of the variable to any particular type and any time you change the right hand side's type the variable would automatically infer the new type.
I only use var when necessary, like:
1) global variables
2) if statement like:
var err error
if x == nil {
err = errors.New("x is nil")
} else if y == nil {
err = errors.New("y is nil")
}
...

Resources