Short declaration in Go - 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.
}

Related

Can I get a mixture of := and = in Go if statements? [duplicate]

This code:
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
var a bool
var b interface{}
b = true
if a, ok := b.(bool); !ok {
fmt.Println("Problem!")
}
}
Yields this error in the golang playground:
tmp/sandbox791966413/main.go:11:10: a declared and not used
tmp/sandbox791966413/main.go:14:21: a declared and not used
This is confusing because of what we read in the short variable declaration golang docs:
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, my questions:
Why can't I redeclare the variable in the code snippet above?
Supposing I really can't, what I'd really like to do is find
a way to populate variables with the output of functions while
checking the error in a more concise way. So is there any way
to improve on the following form for getting a value out of an
error-able function?
var A RealType
if newA, err := SomeFunc(); err != nil {
return err
} else {
A = newA
}
This is happening because you are using the short variable declaration in an if initialization statement, which introduces a new scope.
Rather than this, where a is a new variable that shadows the existing one:
if a, ok := b.(bool); !ok {
fmt.Println("Problem!")
}
you could do this, where a is redeclared:
a, ok := b.(bool)
if !ok {
fmt.Println("Problem!")
}
This works because ok is a new variable, so you can redeclare a and the passage you quoted is in effect.
Similarly, your second code snippet could be written as:
A, err := SomeFunc()
if err != nil {
return err
}
You did redeclare the variable, that is the problem. When you redeclare a variable, it is a different variable. This is known as "shadowing".
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
// original a.
var a bool
var b interface{}
b = true
// redeclared a here. This is a a'
if a, ok := b.(bool); !ok {
// a' will last until the end of this block
fmt.Println("Problem!")
}
// a' is gone. So using a here would get the original a.
}
As for your second question. That code looks great. I would probably switch it to if err == nil (and swap the if and else blocks). But that is a style thing.
The error raised by the compiler is saying that both declarations of variable a are not being used.
You're actually declaring a twice, when using short declaration in the if condition you're creating a new scope that shadows the previous declaration of the variable.
The actual problem you're facing is that you're never using the variable value which, in Go, is considered a compile time error.
Regarding your second question, I think that the shortest way to get the value and check the error is to do something similar to this:
func main() {
a, ok := someFunction()
if !ok {
fmt.Println("Problem!")
}
}

How to declare to variables of different datatypes in a single statement in Go [duplicate]

This question already has answers here:
Multiple variables of different types in one line in Go (without short variable declaration syntax)
(2 answers)
Closed 1 year ago.
I want to declare two variables of different datatypes(string and error) in a single statement in Go. I do not want to use the short declaration(:=) operator because I like specifying the type of the variable at declaration.
I am following a Go tutorial from the Go docs. I have a function called greetings.Hello() that I am calling from another module. The greetings.Hello() function looks like this:
package greetings
import (
"errors"
"fmt"
)
func Hello(name string) (string, error) {
// If no name was given, return an error with a message
if name == "" {
return "", errors.New("empty name")
}
// If a name was received, return a value
var message string = fmt.Sprintf("Welcome %v!", name)
return message, nil
}
So as you can see, this function returns two values(a string and an error). So ultimately, I would have to assign the result of this function to two variables in the caller. I am calling the greetings.Hello() function from a module named hello. The main function of the hello module's main package looks like this:
package main
import (
"fmt"
"log"
"creating_modules/greetings"
)
func main() {
log.SetPrefix("greetings: ")
log.SetFlags(0)
var message string, err error = greetings.Hello("")
if err !=nil {
log.Fatal(err)
}
fmt.Println(message)
}
The creating_modules/greetings is the greetings module that contains the function Hello(). Most of the gophers tackle it like this:
message, error := greetings.Hello()
But I want to declare the variables along with their datatypes in a single statement. Also the two variables should be assigned the return values of greetings.Hello(). The above mentioned main function of the hello module returns an error when it is run because of the incorrect assignment, referring to this line:
var message string, err error = greetings.Hello("")
The Go compiler returns this error when this code is run using go run:
.\hello.go:14:20: syntax error: unexpected comma at end of statement
This issue can simply be reproduced by copy-pasting the code above(note that the greetings module is a local module so you will need to set the reference path for go tools using go edit -replace)
Another thing to be noted is that my question is different from this question because that question is about declaring variables with the same data type in a single statement whereas mine is about declaring multiple variables with different data types in a single statement.
P.S i won't be surprised to know that Golang does not have this feature
declare the variables along with their datatypes in a single statement
Not possible
Supporting clause from the language spec under Variable declarations
If a type is present, each variable is given that type. Otherwise, each variable is given the type of the corresponding initialization value in the assignment. If that value is an untyped constant, it is first implicitly converted to its default type;
So something like below could work by not specifying either of the types, but you could very well use short variable declarations using := instead
var message, error = greetings.Hello()
But you can declare the variables explicitly with their type information and use the = assignment.
var message string
var err error
if message, err = greetings.Hello(""); err != nil {
log.Fatal(err)
}

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
}

Redeclare a variable for convenience in golang?

This code:
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
var a bool
var b interface{}
b = true
if a, ok := b.(bool); !ok {
fmt.Println("Problem!")
}
}
Yields this error in the golang playground:
tmp/sandbox791966413/main.go:11:10: a declared and not used
tmp/sandbox791966413/main.go:14:21: a declared and not used
This is confusing because of what we read in the short variable declaration golang docs:
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, my questions:
Why can't I redeclare the variable in the code snippet above?
Supposing I really can't, what I'd really like to do is find
a way to populate variables with the output of functions while
checking the error in a more concise way. So is there any way
to improve on the following form for getting a value out of an
error-able function?
var A RealType
if newA, err := SomeFunc(); err != nil {
return err
} else {
A = newA
}
This is happening because you are using the short variable declaration in an if initialization statement, which introduces a new scope.
Rather than this, where a is a new variable that shadows the existing one:
if a, ok := b.(bool); !ok {
fmt.Println("Problem!")
}
you could do this, where a is redeclared:
a, ok := b.(bool)
if !ok {
fmt.Println("Problem!")
}
This works because ok is a new variable, so you can redeclare a and the passage you quoted is in effect.
Similarly, your second code snippet could be written as:
A, err := SomeFunc()
if err != nil {
return err
}
You did redeclare the variable, that is the problem. When you redeclare a variable, it is a different variable. This is known as "shadowing".
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
// original a.
var a bool
var b interface{}
b = true
// redeclared a here. This is a a'
if a, ok := b.(bool); !ok {
// a' will last until the end of this block
fmt.Println("Problem!")
}
// a' is gone. So using a here would get the original a.
}
As for your second question. That code looks great. I would probably switch it to if err == nil (and swap the if and else blocks). But that is a style thing.
The error raised by the compiler is saying that both declarations of variable a are not being used.
You're actually declaring a twice, when using short declaration in the if condition you're creating a new scope that shadows the previous declaration of the variable.
The actual problem you're facing is that you're never using the variable value which, in Go, is considered a compile time error.
Regarding your second question, I think that the shortest way to get the value and check the error is to do something similar to this:
func main() {
a, ok := someFunction()
if !ok {
fmt.Println("Problem!")
}
}

Go Global Variables and Scopes of slices

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

Resources