Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Relationship between a package statement and the directory of a .go file

Tags:

import

package

go

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?

like image 433
Lone Learner Avatar asked Apr 24 '17 04:04

Lone Learner


People also ask

What is package directory?

The package Directories provides operations for manipulating files and directories, and their names.

How can you tell Go to import a package from a different location?

Go first searches for package directory inside GOROOT/src directory and if it doesn't find the package, then it looks for GOPATH/src . Since, fmt package is part of Go's standard library which is located in GOROOT/src , it is imported from there.

What is the main significance of package main in Golang?

The package “main” tells the Go compiler that the package should compile as an executable program instead of a shared library. The main function in the package “main” will be the entry point of our executable program.

How are packages used in Go?

Go programs are organized into packages. A package is a collection of source files in the same directory that are compiled together. Functions, types, variables, and constants defined in one source file are visible to all other source files within the same package. A repository contains one or more modules.


2 Answers

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.

like image 140
darethas Avatar answered Oct 11 '22 14:10

darethas


Roughly for the why:

  1. Packages have a "name" which is set by the package clause, the package thepackagename at the start of your source code.

  2. 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".

like image 36
Volker Avatar answered Oct 11 '22 14:10

Volker