I’m back!
I took an extra week off, from what I planned, due to chaotic circumstances, but I’m back again now. I’m also in a new timezone—America/Central. So my daily emails may begin coming on a different erratic schedule than the previous erratic schedule you were accustomed to.
Anyway, let’s get back into the spec!
If you’ve done any Go coding at all, you’ve seen at least one package, the main
package. For the next few days, we’ll be digging into what packages are, and what make them up.
Packages
Go programs are constructed by linking together packages. A package in turn is constructed from one or more source files that together declare constants, types, variables and functions belonging to the package and which are accessible in all files of the same package. Those elements may be exported and used in another package.
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 ";" } .
So each source file starts with a PackageClause (see below for the formal definition of that). But this can be slightly misleading. There are a small number of things that can come before the package
clause:
-
Comments, including GoDoc. It’s good practice to include a descriptive GoDoc paragraph immediately before the
package
clause (i.e. with no blank lines between). You need only do this in a single file (in case of a multi-file package).// Package foo Foos all the Bars. package foo
-
Build constraints and other directives (these are treated as comments, but are worth mentioning separately).
//go:build !js package foo
In this example, we tell the compiler to ignore this file when building for a JS target (i.e. WebAssembly or GopherJS).
-
A shebang line (only as the very first line of the file).
#!/usr/local/bin/go run package main
What?!
Didn’t know that was possible?
This actually isn’t a Go thing at all, it’s more a shell thing. But all common Unix/Linux shells will recognize such a line, and interpret it as the name of an interpretor to pass the file to. You really should not ever need this in Go, but some folks have fun writing Go for shell-script types of tasks. If that describes you, this will apply. I included this here mostly for completeness sake, not as an endorsement of this approach. 😉
Package clause
A package clause begins each source file and defines the package to which the file belongs.
PackageClause = "package" PackageName . PackageName = identifier .
The PackageName must not be the blank identifier.
Honestly, this rule makes me mildly sad. I mean, why can we have blank labels, which are utterly useless, but not blank package names, which would be equally useless? 🤷
package math
A set of files sharing the same PackageName form the implementation of a package. An implementation may require that all source files for a package inhabit the same directory.
This last sentence I find surprises many people. You could write a Go implementation that lets you dump all packages in a single directory, for example, and sorts them out at compile time, and it would be valid according to the Go spec.
Silly, eh? Actually, not enforcing that all package files are in the same directory at the spec level makes good sense in some cases. For example, when you build a Go test, the compiled code comes not only from your *_test.go
files, but from some internal code that augments your code. If the spec required that all package files were in the same directory, this approach would violate the spec.
It could also make practical sense when building code that isn’t in a directory at all—such as when building in the Go playground.
Quotes from The Go Programming Language Specification Language version go1.23 (June 13, 2024)