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:
-
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 ofcmd
named after its usage. (i.e.example.com/project/cmd/clitool
). But it’s required for this package to be calledmain
, to indicate that it is the entry point of the program. -
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 /* ... */
-
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 ofpackage bar
, when that package is imported, the default import name will bebar
. 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 forexample.com/project/foo
, by virtue of the fact that this package is declared aspackage bar
rather thanpackage 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