The universe and package blocks

Let’s now dive into the specific scoping rules in Go. There are 8 rules here, and we’ll take one or two per day… Declarations and scope … Go is lexically scoped using blocks: The scope of a predeclared identifier is the universe block. The “universe block”, which encompasses all Go source text, only contains the predeclared identifiers defined in the specification. Specifically, these identifiers are the names of predeclared types, the constants true, false, and iota, the zero value nil, and the built-in functions.


Scope, defined

Declarations and scope … The scope of a declared identifier is the extent of source text in which the identifier denotes the specified constant, type, variable, function, label, or package. This definition isn’t necessarily specific to Go. Most modern languages have a concept of scope that’s pretty similar. What mainly differs between languages is the rules for where sopes begin and end, or some esoteric features like hoisting in JavaScript.


Where declarations are allowed

First, a big thanks to everyone who showed up for yesterday’s first Boldly Go Live stream. We had some audio problems, which I promise to fix before the next one. But otherwise it was a success, with some great discussion. You can catch the replay if you missed it. Here we are at the formal description of declaration syntax. Declarations and scope … Declaration = ConstDecl | TypeDecl | VarDecl .


Unbound identifiers

There are two identifiers that we can use in Go that do not create a binding to a thing… Declarations and scope … The blank identifier may be used like any other identifier in a declaration, but it does not introduce a binding and thus is not declared. In the package block, the identifier init may only be used for init function declarations, and like the blank identifier it does not introduce a new binding.

Subscribe to Boldly Go: Daily

Every day I'll send you advice to improve your understanding of Go. Don't miss out! I will respect your inbox, and honor my privacy policy.

Unsure? Browse the archive.


Declarations and scope

Declarations and scope A declaration binds a non-blank identifier to a constant, type, type parameter, variable, function, label, or package. Every identifier in a program must be declared. No identifier may be declared twice in the same block, and no identifier may be declared in both the file and package block. That’s a lot of words to basically say: You need to name your things. And your things need unique names.


Implicit blocks

On Monday we saw how to create explicit code blocks in Go. Now we’ll breeze through the list of implicit code blocks that exist. Blocks … In addition to explicit blocks in the source code, there are implicit blocks: The universe block encompasses all Go source text. Each package has a package block containing all Go source text for that package. Each file has a file block containing all Go source text in that file.

Other

1 min read


Watch my first ever Boldly Go Live stream

Next Monday, June 19, I’ll be doing my first ever Boldly Go Live stream on YouTube! I’ll be live coding on a real Go project, and answering audience questions. The event will begin at 3:00pm Central European Summer Time (or 13:00 UTC), and I’ll go for up to two hours, depending on the vibe. You can catch the event here. Drop by early, and click the “Notify Me” link, to be reminded when the event starts.


Blocks

Blocks A block is a possibly empty sequence of declarations and statements within matching brace brackets. Block = "{" StatementList "}" . StatementList = { Statement ";" } . The most common example of an explicit block is the body of a function: func foo() { // <--- The body block start here /* some interesting code goes here */ } // <--- and ends here However, you can also create an explicit block at any time, simply by placing brackes/curly braces ({ and }) around some code:


Defining method sets

Recall that every type in Go has a method set. Let’s examine how that’s defined: Method sets … Every type has a (possibly empty) method set associated with it: The method set of a defined type T consists of all methods declared with receiver type T. type S struct { /* Possibly some fields */ } // Foo() is a member of the method set of `S`, because it has a receiver type `S`.


Every type has a method set

Method sets The method set of a type determines the methods that can be called on an operand of that type. Every type has a (possibly empty) method set associated with it. Wait right there. That bears repeating Every type has a (possibly empty) method set Let that sink in. Every type has a method set. This surprises many newcomers, as in many languages, we’re accustomed to thinking of only “objects” having methods.


Representability

We’ve learned about types, type identity, and type assignability. But you may recall a bit of a discussion about some of the esoteric features of constants in Go. Namely, they can be untyped, and they don’t overflow. So how do we know if a constant can be represented faithfully by a particular type? We finally have the answer, in all its detailed glory: Representability A constant x is representable by a value of type T, where T is not a type parameter, if one of the following conditions applies: