Packages

Packages

Package in Go is a kind of namespace:

  • All code written in Go belongs to a package.

  • Physically, a package is just a directory.

  • Directory names and the package names should be the same.

  • In a package directory, all source files belong to only one package.

  • A source code file should have a package declaration as a first line in a file, always.

  • import keyword allows you to use other packages

Limitations:

  • A package can have a same name with another package it they live inside separate directories.

Features:

  • Let’s you organise your code.

  • Enables reusability by allowing you to use other packages.

  • Helps to enable code encapsulation.

  • Represents a single concept.

  • Small and many.

We will call the packages named with main and containing main entry functions as program packages (or command packages), and call other packages as library packages. Program packages are not importable. Each Go program should contain one and only one program package.

So far, we understood everything there is about packages. Now, let’s combine our understanding of how a program initialises in Go.

go run *.go
├── Main package is executed
├── All imported packages are initialized
|  ├── All imported packages are initialized (recursive definition)
|  ├── All global variables are initialized 
|  └── init functions are called in lexical file name order
└── Main package is initialized
   ├── All global variables are initialized
   └── init functions are called in lexical file name order

At run time, a package will be loaded after all its dependency packages. Each package will be loaded once and only once.

All init functions in all involved packages in a program will be invoked sequentially. An init function in an importing package will be invoked after all the init functions declared in the dependency packages of the importing package for sure. All init functions will be invoked before invoking the main entry function.

The invocation order of the init functions in the same source file is from top to bottom.

All package-level variables declared in a package are initialized before any init function declared in the same package is invoked.

Internal packages

When you name a package as internal, it can only be imported from its parent directory’s packages. If you put into deeper sub-folders, one-step up, its parent packages can access it. Internal package is used to make specific packages unimportable.

project/
  internal/
    foo/
      foo.go # package foo
    bar/
      bar.go # package bar
  main.go

Import internal packages:

package main

import (
  "project/internal/foo"
  "project/internal/bar"
)

For Go Toolchain, a package whose import path containing an internal folder name is viewed as a special package. It can only be imported by the packages in and under the direct parent directory of the internal folder. For example, package .../a/b/c/internal/d/e/f and .../a/b/c/internal can only be imported by the packages whose import paths have a .../a/b/c prefix.

Exports

В Go нет явных модификаторов видимости. Инкапсуляция делается автоматически:

  • Из пакета экспортируются все идентификаторы начинающиеся с заглавной буквы.

  • Все идентификаторы, начинающиеся с прописных букв являются локальными для пакета.

  • Те же правила применяются когда нужно сделать какое-то поле доступом в структуре или интерфейсе.

  • Даже если идентификатор является скрытым внутри пакета его объект может быть возвращен публичной функцией и использован в клиентском коде.

  • Инкапсулируются только имена, а не значения.

Import

Packages are imported with import keyword:

  • An import path is a string that uniquely identifies a package.

  • A package's import path corresponds to its location inside a workspace or in a remote repository.

  • Standard library packages can be imported by its name only (rather than full path).

  • When importing other packages, it’s a good idea to supply the full path.

  • Packages are imported only once. You can import the same package in many packages, and, it will be imported only once.

  • Go does not allow you to have a circular dependency between packages.

Import statement

You can also write multiple import statements:

// Single imports
import "fmt"
import "math"

// Factored imports
import (
    "fmt"
    "math"
)

By convention, the package name is the same as the last element of the import path. For instance, the "math/rand" package comprises files that begin with the statement package rand.

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10))
}

When importing a package, you can refer only to its exported names. Any "unexported" names are not accessible from outside the package.

Blank import (import for side-effects)

import (
    _ "import/package"
)

The blank identifier allows the compiler to accept the import and call any init functions that can be found in the different code files within that package.

This is a technique in Go to allow initialization from a package to occur, even if you don’t directly use any identifiers from the package. To make your programs more readable, the Go compiler won’t let you declare a package to be imported if it’s not used.

Rename on import

import (
    "text/template"
    htemplate "html/template" // this is now imported as htemplate
)

Dot import

It allows the identifiers in the imported package to be referred to in the local file block without a qualifier.

Import declaration Local name of Sin

import   "lib/math"         math.Sin
import M "lib/math"         M.Sin
import . "lib/math"         Sin

Documentation

If you have lengthy comments for the package (such as the extensive formatting documentation in the fmt package), the convention is to put the comments in a file in your package called doc.go.

Last updated