Can someone give me an explanation as to when and why I would use anonymous scope inside a function? (I'm not sure what it's actually called).
I've been given some legacy code to maintain and some of the functions contain this "scope" I have not seen before:
(simplified for demonstration purposes)
func DoSomething(someBoolValue bool) string {
if someBoolValue {
// do some stuff
return "yes"
}
{
// weird scope code
}
return "no"
}
I have created a Go Playground to demonstrate some actual code (that throws an error).
It is called Variable scoping and shadowing:
Go is lexically scoped using blocks:
1-The scope of a predeclared identifier is the universe block.
2-The scope of an identifier denoting a constant, type, variable, or
function (but not method) declared at top level (outside any function)
is the package block.
3-The scope of the package name of an imported
package is the file block of the file containing the import
declaration.
4-The scope of an identifier denoting a method receiver,
function parameter, or result variable is the function body.
5-The scope of a constant or variable identifier declared inside a function
begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short
variable declarations) and ends at the end of the innermost containing
block.
6-The scope of a type identifier declared inside a function
begins at the identifier in the TypeSpec and ends at the end of the
innermost containing block. 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.
The package clause is not a declaration; the package name does not
appear in any scope. Its purpose is to identify the files belonging to
the same package and to specify the default package name for import
declarations.
your working sample code:
package main
import (
"fmt"
)
func main() {
i := 10
{
i := 1
fmt.Println(i) // 1
}
fmt.Println(i) // 10
}
output:
1
10
and see: Where can we use Variable Scoping and Shadowing in Go?
{} in Go form a Syntactic Block. Each block defines a new scope. These are same blocks that you use with if and for for example.
With respect to your code, I guess they exist mainly for readability purpose. You can reuse or hide variable defined in the enclosing scope to make use of variables names that maybe declare the code's intent more clearly.
Other than that they can also be used to group a bunch of related statements, again for readability.
To understand their behavior, refer to #Amd's answer.
Related
As I'm writing DB migrations using gormigrate, I need to define a many-to-many relationship between two structs in a function scope. But in golang 1.19 or 1.18 the following won't compile
package main
import "fmt"
func main() {
type Student struct {
Courses []*Course
// [Error] ./prog.go:7:14: undefined: Course
}
type Course struct {
Students []*Student
}
fmt.Printf("This won't compile")
}
However moving the definitions outside of the function will just work
package main
import "fmt"
type Student struct {
Courses []*Course
}
type Course struct {
Students []*Student
}
func main() {
fmt.Printf("This works")
}
Can try this yourself at https://go.dev/play/p/GI53hhlUTbk
Why is this the case? And how can I make it work in a function scope?
Is there a similar syntax to typedef in C++, so we can declare a struct first and then define it later?
Thanks!
Circular type references are possible in the package block, but not inside a function. The section on Declarations and Scopes in the specification says:
The scope of an identifier denoting a constant, type, variable, or function (but not method) declared at top level (outside any function) is the package block.
⋮
The scope of a type identifier declared inside a function begins at the identifier in the TypeSpec and ends at the end of the innermost containing block.
Circular references work at package-level because types declared at package-level are scoped to the entire package block.
The scope of a type declared in a function begins at the declaration, not at the beginning of the containing block. A type cannot reference types declared later in the function because those types are not in scope.
It follows that circular type references are not allowed for types declared in a function.
There is not a way to declare the name of a type and later define the type.
If I use variable inside a function then Go gives error otherwise not please check 2 examples.
GIVES ERROR n declared but not used
https://play.golang.org/p/z-EktUDkNDz
package main
func main() {
var n int
n = 10
}
NO Error when var declared outside the function https://play.golang.org/p/nFSEoktcE5e
package main
var n int
func main() {
n = 10
}
That's as per the standard:
Implementation restriction: A compiler may make it illegal to declare a variable inside a function body if the variable is never used.
Reference: https://golang.org/ref/spec#Variable_declarations
Please make a note that it states "may make". That means it's up to a particular compiler implementation. But in general it's better to assume it's not allowed.
And there is no such similar restriction for variables declared in a global scope.
Does fmt.Println need to always belong to a function?
Have used Python before and it allows it but on research, it seems that Java doesn't
fmt.Println("can I do it?")
Returns:
syntax error: non-declaration statement outside function body
It may be outside of a function, see this example:
var n, err = fmt.Println("I can do it")
func main() {
fmt.Println("In main(),", n, err)
}
It outputs (try it on the Go Playground):
I can do it
In main(), 12 <nil>
(The output values 12 <nil> are the values returned by the first fmt.Println() call, the number of bytes it has written and the error it returned which is nil indicating no error.)
Also note that you don't even have to store the return values of fmt.Prinln(), you can use the blank identifier like this:
var _, _ = fmt.Println("I can do it")
It cannot stand on its own at the top level "between" top-level declarations, but the above variable declaration (with blank identifier) pretty much achieves the same.
Spec: Source file organization:
Each source file consists of a package clause defining the package to which it belongs, followed by a possibly empty set of import declarations that declare packages whose contents it wishes to use, followed by a possibly empty set of declarations of functions, types, variables, and constants.
SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
Obviously a package clause or import declaration can't contain an fmt.Println() call, and the top level declarations:
Declaration = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
A constant declaration cannot contain an fmt.Println() call, that's not a constant expression. A type declaration also cannot contain function calls.
A variable declaration can, as shown in the example at the top of the answer.
Function and method declarations could also call fmt.Println(), but you were asking specifically if fmt.Println() can be called outside of them.
So the only place it is allowed outside of functions that is allowed at the top level is in variable declarations.
go always starts execution in the main function, so fmt.Println() need to be in the main function or a function that is called in main.
I am going through the Go specification to learn the language, and these points are taken from the spec under Declarations and scope.
Though I am able to understand points 1-4, I am confused on points 5 and 6:
The scope of a constant or variable identifier declared inside a
function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
for short variable declarations) and ends at the end of the
innermost containing block.
The scope of a type identifier declared inside a function begins at
the identifier in the TypeSpec and ends at the end of the innermost
containing block.
This is the code which I used to understand scope in Go:
package main
import "fmt"
func main() {
x := 42
fmt.Println(x)
{
fmt.Println(x)
y := "The test message"
fmt.Println(y)
}
// fmt.Println(y) // outside scope of y
}
From this what I understand is scope of x is within the main function, and the scope of y is inside the opening and closing brackets after fmt.Println(x), and I cannot use y outside of the closing brackets.
If I understand it correctly, both points 4 and 5 are saying the same thing. So my questions are:
If they are saying the same thing, what is the importance of both the
points?
If they are different, can you please let me know the difference?
They're making the same point, with the same rules, about two different things: the first is about variables and constants, the second is about type identifiers. So, if you declare a type inside a block, the same scoping rules apply as would apply to a variable declared at the same spot.
Besides applying to different things (rule #5 is for constant- and variable declarations, rule #6 is for type declarations), there is also an important difference in wording:
The scope of a constant or variable identifier declared inside a
function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
for short variable declarations) and ends at the end of the
innermost containing block.
The scope of a type identifier declared inside a function begins at
the identifier in the TypeSpec and ends at the end of the innermost
containing block.
This is the reason why there are 2 rules and not one.
What does this mean? What does the difference imply?
# 5 Variable and Constant declarations (inside a function)
The scope of the declared variables or constants begins at the end of the declaration. This means if you're creating a function variable, initializing it with an anonymous function, it can't refer to itself.
This is invalid:
f := func() {
f()
}
Attempting to compile:
prog.go:5:3: undefined: f
This is because the declaration ends after the closing bracket of the anonymous function, so inside of it you can't call f(). A workaround would be:
var f func()
f = func() {
f()
}
Now here the declaration of f ends at the closing parenthesis (of its type func()), so in the next line when we assign an anonymous function to it, it is valid to refer to f (to call the function value stored in the f variable) because it is now in scope. See related question: Define a recursive function within a function in Go
Similarly, when initializing a variable e.g. with a composite literal, you can't refer to the variable inside of it:
var m = map[int]string{
1: "one",
21: "twenty-" + m[1],
}
This gives a compile-time error ("undefined: m"), because m is not in scope yet inside the composite literal.
And obviously this workaround works:
var m = map[int]string{
1: "one",
}
m[21] = "twenty-" + m[1]
# 6 Type declarations (inside a function)
The scope of the declared type begins at the identifier in the declaration. So in contrast to rule #5, it is valid to refer to the type itself inside its declaration.
Does it have any advantage / significance?
Yes, you can declare recursive types, such as this:
type Node struct {
Left, Right *Node
}
The type identifier Node is in scope right after it appears in the type declaration, which ends with the closing bracket, but before that we could refer to it, meaningfully.
Another example would be a slice type whose element type is itself:
type Foo []Foo
You can read more about it here: How can a slice contain itself?
Yet another valid example:
type M map[int]M
While calling json.Decoder.Decode by passing a struct, for instance
type _Sample struct {
first string // this will not be filled because it starts with lower case letter
Second string // it is OK.
}
...
var sample _Sample
err := decoder.Decode(&sample)
According to the Language Specification that writes:
Exported identifiers ¶
An identifier may be exported to permit access to it from another package. An identifier is exported if both:
the first character of the identifier's name is a Unicode upper case letter (Unicode class "Lu"); and
the identifier is declared in the package block or it is a field name or method name.
All other identifiers are not exported.
The question is the struct _Sample is not exported, why it is visible by the package json?
Access permission is checked when
You're creating/declaring a variable. The compiler will check the visibility of the name of the type identifier.
Using reflect package to modify a variable.
Calling a constant, global variable, or a function. The visibility of constant/variable/function name will be checked.
Once the variable is created/defined, and when you pass this variable as a function/method argument, the copy (If it is a value, the value is being copied. If it is a pointer, the address is being copied) will be passed to the function/method, and always accessible from the function body through the argument name and type. In encoding/json package, the Decoder.Decode method is defined as,
func (dec *Decoder) Decode(v interface{}) error
thus, anything you passed to the Decoder.Decode method, is always accessible from inside the method body through argument v. Note that the type of v is interface{} not _Sample struct. The visibility is checked when you defined the variable as
var sample _Sample
which was OK since it is done from within the same package. Even an anonymous struct (i.e. no type name identifier is defined for the struct)
aSample := struct {
first string // this will not be filled because it starts with lower case letter
Second string // it is OK.
}{}
//...
err := decoder.Decode(&aSample)
will works. Next, when the decoder fills the struct (through reflection), the visibility of struct members will be checked, and _Sample.first is not visible from inside the json package.