Import declarations

October 15, 2024

Today we have a very important topic…

Import declarations

An import declaration states that the source file containing the declaration depends on functionality of the imported package (§Program initialization and execution) and enables access to exported identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported.

ImportDecl       = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec       = [ "." | PackageName ] ImportPath .
ImportPath       = string_lit .

The PackageName is used in qualified identifiers to access exported identifiers of the package within the importing source file. It is declared in the file block. If the PackageName is omitted, it defaults to the identifier specified in the package clause of the imported package. If an explicit period (.) appears instead of a name, all the package’s exported identifiers declared in that package’s package block will be declared in the importing source file’s file block and must be accessed without a qualifier.

This is a good place to stop and parse what we’ve just read.

An import declaration starts with the word import, followed by one or more import specs. If more than one, the specs must be wrapped in parenthesis. Therefore, following snippets are equivalent:

import "fmt"
import "math"
import (
  "fmt"
  "math"
)
import (
  "fmt"
)

import (
  "math"
)

The import spec is where things start to get more interesting. It contains only two parts, but there are some subtleties to consider. Let’s do that by looking at some examples, starting with the simplest.

import foo "example.org/foo"

Here we have an import spec with an explicit package name, and import path. There’s no ambguity here at all. If you want to call function Bar in the imported package, you do it by using the package name:

foo.Bar( /* ... */ )

But that foo package name looks redundant, right? Can’t we omit it?

import "example.org/foo"

Short answer: Probably. Long answer: Not so fast! The default package name is taken from the package clause in the imported package. And while it is convention to name a package the same as the last element of an import path, it’s not required. So if we were to open up the imported package and see the following:

package bar

then this would fail, unless we update foo.Bar to bar.Bar:

import "example.org/foo"

main() {
  bar.Bar( /* ... */ )
}

Well ain’t that confusing. There’s no way, other than guessing, to determine where that bar thing comes from. Consider this when naming your packages!

Quotes from The Go Programming Language Specification Language version go1.23 (June 13, 2024)


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Working with versioned imports

We’re continuing today our discussion of imports. Yesterday we left off with using explicit package names in an import clause when the package name differs from the last element of the import path. Is that the only reason to explicitly name your imports? No. Sometimes you need to disambiguate between two imports with the same package name. Or maybe you just like a shorter name. import ( log "github.com/sirupsen/logrus" // Shorter names are nice stdlog "log" // Oh, but we need to reference this package, too ) There is one exception to the rule “Name your packages the same as the last element of your import path,” though: v2+ releases.


Import cycles

First off, the spec provides an example of how to reference a symbol in an imported package, using different import alias options. I’ve already provided my own examples earlier, and it’s pretty straight forward, so we’ll just breeze through this part. Import declarations … Consider a compiled a package containing the package clause package math, which exports function Sin, and installed the compiled package in the file identified by "lib/math". This table illustrates how Sin is accessed in files that import the package after the various types of import declaration.


Valid import paths

Today we’re looking at one of the more essoteric parts of the spec. Import declarations … Implementation restriction: A compiler may restrict ImportPaths to non-empty strings using only characters belonging to Unicode’s L, M, N, P, and S general categories (the Graphic characters without spaces) and may also exclude the characters !"#$%&’()*,:;<=>?[]^`{|} and the Unicode replacement character U+FFFD. Other than what’s stated in the quote, we’re not going to look at an exhaustive list of what is and isn’t guaranteed to be supported by a Go implementation.

Get daily content like this in your inbox!

Subscribe