Scope of the package clause

June 30, 2023

If you’ve written any code in Go, you’ve seen the package clause:

package main

So what is the scope of this clause?

Declarations and scope

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.

If it has no scope, why is it needed?

Well, that’s a good question. I’m not entirely sure. An argument could be made that the package name could be inferred from the directory name. In fact, by convention, the package name “should” match the directory name.

There are three situations where this doesn’t follow, though, which I suppose all stand in as reasons to explicitly declare the package name:

  1. The main package. It’s rare to put your main package in a main subdirectory. It most frequently lives in the top level directory of your project, or in a subdirectory of cmd named after its usage. (i.e. example.com/project/cmd/clitool). But it’s required for this package to be called main, to indicate that it is the entry point of the program.

  2. Test packages. Test code in Go is meant to live in the same directory as the code it is testing. But sometimes, you want to test your code from an external package (i.e. for “black-box” testing). You can accomplish this by using the special *_test package. For example, given this file to test:

    package foo
    
    /* ... */
    

    You could place your tests in a file like this:

    package foo_test
    
    /* ... */
    
  3. The package name serves as the default import name. Honestly, I think this feature is confusing, and perhaps even a “mistake”. But it’s where we are. And here’s what it means. Given a file in directory foo, but a packgae clause of package bar, when that package is imported, the default import name will be bar. This can lead to the following confusion:

    package main
    
    import "example.com/project/foo"
    
    func main() {
    	bar.Run()
    }
    

    WAT? Where is that bar defined? Why, that’s the import name for example.com/project/foo, by virtue of the fact that this package is declared as package bar rather than package foo.

    Some tools, such as gofumpt, will helpfully update such import statements to be more clearly labeled:

    import bar "example.com/project/foo"
    

    But from the standpoint of the spec, that is optional.

One last argument in favor of requiring the explicit package clause is as a simple check. More than once I’ve accidentally placed a new file in the wrong directory, and received an error along the lines of:

found packages foo (foo.go) and main (main.go) in /home/jonhall/project

Arguably, I would have found my error eventually, so I’m not sure this is a very compelling reason.

Therefore, I would be in favor of making the package clause optional. But I don’t think my opinion counts for much. 😊

Quotes from The Go Programming Language Specification Version of December 15, 2022


Share this

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

Unsure? Browse the archive .

Related Content


Uniqueness of identifiers

Uniqueness of identifiers Given a set of identifiers, an identifier is called unique if it is different from every other in the set. Two identifiers are different if they are spelled differently, or if they appear in different packages and are not exported. Otherwise, they are the same. This is pretty simple stuff, but it’s obviously worth being explicit. foo and bar are obviously different identifiers. Further, a in package foo, and a in package bar are different identifiers, because although they are spelled the same, they are not exported, and are in different packages.


init functions

We’ve been looking at the order of package variable initialization. What if these rules are confusing? Or even non-desterministic for you? Or maybe you simply want to do something more advanced than is feasible in package variable declarations. Is there an alternative? There is! Package initialization … Variables may also be initialized using functions named init declared in the package block, with no arguments and no result parameters. func init() { … } Note that package variables can be initialized, but not declared within an init function.


When package variable initialization order isn't defined

We’re nearing the end of the discussion on package initialization order. Monday should be the last day on that topic, but more on that shortly. Up to now, we’ve been looking at the deterministic order-of-initialization rules.Today’s topic is when that order is not defined. Package initialization … Dependency analysis is performed per package; only references referring to variables, functions, and (non-interface) methods declared in the current package are considered. If other, hidden, data dependencies exists between variables, the initialization order between those variables is unspecified.

Get daily content like this in your inbox!

Subscribe