Imagine you have more than a thousand functions which you need constantly while working on any project. Some of these functions have common behavior. For example, toUpperCase and toLowerCase function transforms case of a string, so you write them in a single file (probably case.go). There are other functions which do some other operations on string data type, so you write them in a separate file as well.
Since you have many files which do something with string data type, so you created a directory named string and put all string related files into it. Finally, you put all of these directories in one parent directory which will be your package. The whole package structure looks like below.
I will explain thoroughly, how we can import functions and variables from a package and how everything blends together to form a package, but for now, imagine your package as a directory containing .go files.
Every Go program must be a part of some package.. If a program is part of the main package, then go install will create a binary file; which upon execution calls main function of the program. If a program is part of package other than main, then a package archive file is created with go install command. Don’t worry, I will explain all this in upcoming topics.
Let’s create an executable package. As we know, to create a binary executable file, we need our program to be a part of main package and it must have main function which is the entry point of execution.
A package name is the name of the directory contained in src directory. In the above case, app is the package since app is the child directory of srcdirectory. Hence, go install app command looked for app sub-directory inside src directory of GOPATH. Then it compiled the package and created app binary executable file inside bin directory which should be executable from the terminal since bin directory in the PATH.
Package declaration which should be first line of code like package main in above example, can be different than package name. Hence, you might find some packages where package name (name of the directory) is different than package declaration. When you import a package, package declaration is used to create package reference variable, explained later in the article.
go install <package> command looks for any file with main package declaration inside given package directory. If it finds a file, then Go knows this is an executable program and it needs to create a binary file. A package can have many files but only one file with main function, since that file will be the entry point of the execution.
If a package does not contain a file with main package declaration, then Go creates a package archive (.a) file inside pkg directory.
Since, app is not an executable package, it created app.a file inside pkgdirectory. We can not execute this file as it’s not a binary file.
Package naming convention
Go community recommends to use plain and simple names for packages. For example, strutils for string utility functions or http for HTTP requests related functions. A package names with under_scores, hy-phens or mixedCaps should be avoided.
As we discussed, there are two types of packages. An executable packageand a utility package. An executable package is your main application since you will be running it. A utility package is not self-executable, instead, it enhances the functionality of an executable package by providing utility functions and other important assets.
As we know, a package nothing but a directory, let’s create greet directory inside src and create few files in it. This time, we will write package greet a declaration on the top of each file to state that this is a utility package.
Now, we need an executable package which will consume our greetpackage. Let’s create an app directory inside src and create entry.go file with main package declaration and main function. Note here, Go packages do not have an entry file naming system like index.js in Node. For an executable package, a file with main function is the entry file for execution.
To import a package, we use import syntax followed by the package name. 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. Since Go cannot find greet package inside GOROOT, it will lookup inside GOPATH/src and we have it there.
Coming back to our main issue, we can use variable version declared in version.go file from anywhere in the package even though it is not exported (like Version), because it is declared in package scope. If version variable would have been declared in a function, it wouldn’t have been in package scope and above program would have failed to compile.
You are not allowed to re-declare global variable with the same name in the same package. Hence, once version variable is declared, it can not be re-declared in the package scope. But you are free to re-declare elsewhere.
In the above example, greet/greet package now is referenced by childvariable. If you notice, we aliased greet package with an underscore. Underscore is a special character in Go which acts as null container. Since we are importing greet package but not using it, Go compiler will complain about it. To avoid that, we are storing reference of that package into _ and Go compiler will simply ignore it.
Aliasing a package with an underscore which seems to do nothing is quite useful sometimes when you want to initialize a package but not use it.