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.
Related
could someone be so kind to explain this behaviour to me? I can't wrap my mind around why this happens (still learning Go). My concrete questions are marked in the source code with QUESTION.
Thanks,
Michael
package main
// Define a simple type and functions on this type.
type Foo struct{}
var foo *Foo
func (f *Foo) function() {
if f == nil {
panic("Why is f nil?")
}
}
// Create a wrapper struct containg method pointers to a global receiver variable.
type Example struct {
f func()
}
var bar = &Example{
// QUESTION: When is foo actually evaluated? It seems at definition of bar and then it's fixed?
// QUESTION: And why is its value at runtime not used to determine the (now initialized) receiver target?
f: foo.function,
}
// Initialize the global variable.
func init() {
foo = new(Foo)
}
// Call the function on foo, which should be initialized in init.
func main() {
bar.f()
}
This is the relevant section in the language spec:
https://golang.org/ref/spec#Package_initialization
All global variables are initialized based on dependency analysis. So when this initializes:
var bar = &Example{
f: foo.function
}
it requires foo to be initilized. Since there are no initializers defined for foo, it is nil. Once all variable initialization is complete, init() function runs and sets foo to non-nil value.
If you change the declaration to:
var foo = new(Foo)
it works.
This question already has answers here:
Variable scope inside if statements
(2 answers)
Closed 4 years ago.
Can someone tell me why num is undefined :: Here is go playground link also you can check this code here:
https://play.golang.org/p/zR9tuVTJmx-
package main
import "fmt"
func main() {
if 7%2 == 0 {
num := "first"
} else {
num := "second"
}
fmt.Println(num)
}
That's something related to lexical scoping, look over here for an introduction
Basically any variable within {}curly braces is considered as new variable, within that block.
so In the above program you have created two new variables.
Block is something like enclosing a around a variable.
If you are outside a block, you cannot see it. you need to be inside the block in order to see it.
package main
import "fmt"
func main() {
if 7%2 == 0 {
// you are declaring a new variable,
num := "first"
//this variable is not visible beyond this point
} else {
//you are declaring a new variable,
num := "second"
//this variable is not visible beyond this point
}
// you are trying to access a variable, which is declared in someother block,
// which is not valid, so undefined.
fmt.Println(num)
}
What you are looking for is this:
package main
import "fmt"
func main() {
num := ""
if 7%2 == 0 {
//num is accessible in any other blocks below it
num = "first"
} else {
num = "second"
}
//num is accessible here as well, because we are within the main block
fmt.Println(num)
}
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
Consider this package:
package A
var X="change me"
var Y=func(i int) int { return i*i) }
func Z(i int) int { return -i) }
The two explicit variables (X,Y) can be changed in another package, say main...
package main
import "A"
func main () {
A.X="done"
A.Y=func (i int) int { return i*i*i }
print(A.X,A.Y(7))
//... but A.Z apparently can't be changed.
//A.Z=func (int i) int { return i*i*i } //main.go:8: cannot assign to A.Z
}
Obviously there's a difference between defining a func variable (like Y) and an explicit func (like Z). I have googled this but not found much in the way of enlightenment. It almost seems as if var SomeFunc=func (...) defines indeed a variable, but func SomeFunc(...) defines a constant.
PS: A small goodie I found while researching this which I have not seen mentioned in the Go books I've read so far. A dot before a package import imports names without them having to be qualified:
package main
import . "A"
func main () {
X="done"
Y=func (i int) int { return i*i*i }
print(X,Y(7))
}
func SomeFunc(), in essence creates a strong/constant/immutable binding of the identifier SomeFunc to the function you define. When you create a variable like so:
var (
SomeFunc = func(i int) int {
return i * 2
}
)
You create a global variable of the type func(int) int. You can reassign this variable later on. This is something you can't really do with a func SomeFunc identifier. Simply put, this is because func SomeFunc() binds the function Directly to the identifier. The var SomeFunc approach creates a variable (type func(int) int in this case), and that variable is initialised using the function you're assigning. As is the case with variables: reassignment is possible.
Example
What you can do with functions, is shadow them using a scoped variable. This will probably get flagged by most linters, but it's a technique/trick that sometimes can be useful in testing
Example
As for the dot-imports: Please don't do that unless there's a very, very, very good reason for it. A good reason would be you writing a package that adds to an existing one, so you no longer import an existing one, but import your own. Think of it as extending a package. 99% of the time. Don't, whatever you do, use it to quench errors when you import encoding/json to add json serialization annotations to a struct. In those cases, use an underscore:
package foo
import (
"encoding/json"
)
type Bar struct {
Foobar string `json:"foobar"`
}
func New() *Bar {
&Bar{"Default foobar"}
}
Don't know about golang 1.8, but packages like that could result in compiler errors (package encoding/json imported but not used). To silence that error, you simply changed the import to:
import(
_ "encoding/json"
)
The dot-packages, underscores, and package aliases all follow the same rule: use them as little as possible.
Code used in examples:
package main
import (
"fmt"
)
var (
SomeFunc = func(i int) int {
return i * 2
}
)
func main() {
fmt.Println(SomeFunc(2)) // output 4
reassign()
fmt.Println(SomeFunc(2)) // output 8
shadowReassign()
fmt.Println(SomeFunc(2)) // output 2
}
// global function
func reassign() {
// assign new function to the global var. Function types MUST match
SomeFunc = func(i int) int {
return i * 4
}
}
// assign function to local reassign variable
func shadowReassign() {
reassign := func() {
// same as global reassign
SomeFunc = func(i int) int {
return i
}
}
reassign()
}
There's a difference between declaring a variable initialized with a function value:
var Y=func(i int) int { return i*i) }
and declaring a function:
func Z(i int) int { return -i) }
The specification says this about declarations:
A declaration binds a non-blank identifier to a constant, type, variable, function, label, or package.
The specification also says:
A function declaration binds an identifier, the function name, to a function.
The declaration of Y binds a variable to the name. This variable is initialized with a function value. The declaration of Z binds a function to the name.
If an explicit period (.) appears instead of a name, all the package's exported identifiers declared in that package's package block will be declared in the importing source file's file block and must be accessed without a qualifier.
When retrieving multiple returns from a function, I get that you can declare variables for the values on the fly by using := or assign the values to already existing variables by simply using =. My issue occurs when I want to assign one of the return values to an already existing variable while declaring a new variable for the other.
I have currently solved it by only assigning the values and declaring the required variables (bar in this case) beforehand, as in this snippet:
package main
import (
"fmt"
)
func getFooAndBar() (foo string, bar string) {
return "Foo", "Bar"
}
func main() {
var foo = "default"
var condition = true
if condition {
var bar string // Would like to avoid this step if possible
foo, bar = getFooAndBar()
fmt.Println(bar)
}
fmt.Println(foo)
}
If I use := it fails to build due to:
./app.go:16: foo declared and not used
So, is it possible to somehow avoid the step declaring bar separately?
In this case you can't use the short variable declarations ":=" for redeclaring the foo variable, according to the spec:
Unlike regular variable declarations, a short variable declaration may
redeclare variables provided they were originally declared earlier in
the same block 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.
by eliminating ./app.go:16: foo declared and not used.
func main() {
var foo = "default"
var condition = true
if condition {
foo, bar := getFooAndBar()
fmt.Println(bar) // prints: Bar
fmt.Println(foo) // prints: Foo
// _ = foo
}
fmt.Println(foo) // prints: default
}
in this case foo is declared in the if block, this declaration will create a new variable shadowing the original foo variable in the outer block, the redeclaration of foo will happen only if you have declared foo and redeclared it with multi-variable short declaration within the same block.
func main() {
var foo = "default"
foo, bar := getFooAndBar()
fmt.Println(bar) //prints: Bar
fmt.Println(foo) //prints: Foo
}