Reputation: 20618
See this experiment.
~/go/src$ tree -F
.
├── 1-foodir/
│ └── 2-foofile.go
└── demo.go
1 directory, 2 files
~/go/src$ cat demo.go
package main
import (
"fmt"
"1-foodir"
)
func main() {
fmt.Println(foopkg.FooFunc())
}
~/go/src$ cat 1-foodir/2-foofile.go
package foopkg
func FooFunc() string {
return "FooFunc"
}
~/go/src$ GOPATH=~/go go run demo.go
FooFunc
I thought that we always import a package name. But the above example
shows that we actually import a package directory name ("1-foodir"
)
but while invoking exported names within that package, we use the
package name declared in the Go files (foopkg.FooFunc
).
This is confusing for a beginner like me who comes from Java and Python world, where the directory name itself is the package name used to qualify modules/classes defined in the package.
Why is there a difference in the way we use import
statement and
refer to names defined in the package in Go? Can you explain the rules
behind these things about Go?
Upvotes: 51
Views: 20937
Reputation: 24866
package xxx
declare the package name, which is the default alias when import.import path/to/dir
will use the dir path, not necessarily package name.package xxx
.
package __
(double underscore), then:
(The common base dir for files below is eric/go/pkg_learn/pkg_dir_diff
)
Source file tree:
eric/go/pkg_learn/pkg_dir_diff
├── d
│ └── d.go
├── d1
│ └── d1.go
├── d2
│ └── d2.go
├── pkg_1_and_2_test.go
├── pkg_1_test.go
└── pkg_2_test.go
eric/go/pkg_learn/pkg_dir_diff/d/d.go
package __
func D() {
println("d")
}
eric/go/pkg_learn/pkg_dir_diff/d1/d1.go
package d
import d2 "eric/go/pkg_learn/pkg_dir_diff/d2"
func One() {
println("one")
d2.Two()
}
func OneTwo() {
One()
d2.Two()
}
eric/go/pkg_learn/pkg_dir_diff/d2/d2.go
package d
func Two() {
println("two")
}
eric/go/pkg_learn/pkg_dir_diff/pkg_1_test.go
package pkg_learn
import (
d "eric/go/pkg_learn/pkg_dir_diff/d1"
"testing"
)
func TestSamePkgFromDifferentDirs1(t *testing.T) {
d.One()
}
eric/go/pkg_learn/pkg_dir_diff/pkg_2_test.go
package pkg_learn
import (
d "eric/go/pkg_learn/pkg_dir_diff/d2"
"testing"
)
func TestSamePkgFromDifferentDirs2(t *testing.T) {
d.Two()
}
eric/go/pkg_learn/pkg_dir_diff/pkg_1_and_2_test.go
package pkg_learn
import (
d "eric/go/pkg_learn/pkg_dir_diff/d"
d1 "eric/go/pkg_learn/pkg_dir_diff/d1"
d2 "eric/go/pkg_learn/pkg_dir_diff/d2"
"testing"
)
func TestSamePkgFromDifferentDirs1and2(t *testing.T) {
d1.One()
d2.Two()
d1.OneTwo()
}
func TestD(t *testing.T) {
d.D()
}
Upvotes: -1
Reputation: 3105
First thing first, the package clause and import path are different things.
package clause declares PackageName
:
PackageClause = "package" PackageName .
PackageName = identifier .
The purpose of a package clause is to group files:
A set of files sharing the same PackageName form the implementation of a package.
By convention, the path basename (directory name) of ImportPath
(see below) is the same as PackageName
. It's recommended for convenient purposes that you don't need to ponder what's the PackageName
to be used.
However, they can be different.
The basename only affects the ImportPath
, check spec for import declartions:
ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec = [ "." | PackageName ] ImportPath .
ImportPath = string_lit .
If the PackageName is omitted, it defaults to the identifier specified in the package clause of the imported package.
For example, if you have a dir foo
but you declare package bar
in a source file resides in it, when you import <prefix>/foo
, you will use bar
as a prefix to reference any exported symbols from that package.
darehas' answer raises a good point: you can't declare multiple packages under the same basename. However, according to package clause, you can spread the same package over different basenames:
An implementation may require that all source files for a package inhabit the same directory.
Upvotes: 8
Reputation: 7657
If what you said was true, then your function call would actually be 1-foodir.FooFunc()
instead of foopkg.FooFunc()
. Instead, go sees the package name in 2-foofile.go and imports it as foopkg
because in go the name of the package is exactly what comes after the words package
at the top of .go files, provided it is a valid identifier.
The only use of the directory is for collecting a set of files that share the same package name. This is reiterated in the spec
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.
In go, it is convention that the directory match the package name but this doesn't have to be the case, and often it is not with 3rd party packages. The stdlib does do a good job of sticking to this convention.
Now where directories do come into play is the import path. You could have 2 packages named 'foo' in your single binary as long as they had different import paths, i.e.
/some/path/1/foo
and /some/path/2/foo
And we can get really swanky and alias the imports to whatever we wanted, for example I could do
import (
bar "/some/path/1/foo"
baz "/some/path/2/foo"
)
Again the reason this works is not because the package name has to be unique, but the package import path must be unique.
Another bit of insight to glean from this statement is -- within a directory, you cannot have two package names. The go compiler will throw an error stating it cannot load package
and that it found packages foo (foo.go) and bar (bar.go)
.
See https://golang.org/doc/code.html#PackageNames for more information.
Upvotes: 38
Reputation: 42433
Roughly for the why:
Packages have a "name" which is set by the package clause, the package thepackagename
at the start of your source code.
Importing packages happens by pretty much opaque strings: the import path in the import declarations.
The first is the name and the second how to find that name. The first is for the programmer, the second for the compiler / the toolchain. It is very convenient (for compilers and programmers) to state
Please import the package found in
"/some/hierarchical/location"
and then refer to that package by it's simple name like robot
in statements like
robot.MoveTo(3,7)
Note that using this package like
/some/hierarchical/location.MoveTo(3.7)
would not be legal code and neither readable nor clear nor convenient. But to for the compiler / the toolchain it is nice if the import path has structure and allows to express arbitrary package locations, i.e. not only locations in a filesystem, but e.g. inside an archive or on remote machines, or or or.
Also important to note in all this: There is the Go compiler and the go
tool. The Go compiler and the go tool are different things and the go tool imposes more restrictions on how you lay out your code, your workspace and your packages than what the Go compiler and the language spec would require. (E.g. the Go compiler allows to compile files from different directories into one package without any problems.)
The go
tool mandates that all (there are special cases, I know) your source files of a package reside in one file system directory and common sense mandates that this directory should be "named like the package".
Upvotes: 11