Packages

October 14, 2024

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)


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


An example package

Today we have something a bit different. Just a sample package. An example package Here is a complete Go package that implements a concurrent prime sieve. package main import "fmt" // Send the sequence 2, 3, 4, … to channel 'ch'. func generate(ch chan<- int) { for i := 2; ; i++ { ch <- i // Send 'i' to channel 'ch'. } } // Copy the values from channel 'src' to channel 'dst', // removing those divisible by 'prime'.


Import declarations

Today we have a very important topic… Import declarations An import declaration states that the source file containing the declaration depends on functionality of the imported package (§Program initialization and execution) and enables access to exported identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported. ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .


Tuple assignments

Yesterday (as well as in earlier emails) I mentioned multiple-assignments in a single statement. Today we see how they’re defined to work. As such, there’s not really an new material here, but we’ll cover it just the same. Assignment statements … A tuple assignment assigns the individual elements of a multi-valued operation to a list of variables. There are two forms. In the first, the right hand operand is a single multi-valued expression such as a function call, a channel or map operation, or a type assertion.

Get daily content like this in your inbox!

Subscribe