skip to content

In a Go import, which identifier does the import path bind, and when do you alias it?

level: middleimportance: should knowfreq 52%

answer

  1. the path is a string, not a name
  2. the package clause decides
  3. same name twice forces a rename
  4. crypto/rand and math/rand collide

basics

~20 s

An import path only locates the package. The identifier bound in that file is the package's own declared name from its package clause, which usually but need not match the last path element. Alias when two imports would bind the same name.

solid answer

~40 s

`import "math/rand"` binds `rand` because that package's clause says `package rand`, not because of the path's last element; the two merely coincide by convention. They diverge for paths whose last element is a major-version marker such as `/v2`, and for directories whose names are not valid Go identifiers. Imports are file-scoped, so every file states its own and may name it differently. An alias — `import crand "crypto/rand"` — is required when two paths would bind the same identifier, which is exactly what `crypto/rand` and `math/rand` do, and is otherwise used sparingly, because renaming a well-known package costs the reader. Two special forms complete the set: `_` binds nothing and imports purely for side effects, and `.` drops the package's exported names into the file's scope, which is discouraged outside generated or example code.

code

go · 13 lines
go
import (
	crand "crypto/rand"
	"math/rand"
)

func jitter(max int) int {
	return rand.Intn(max) // math/rand, still bound as rand
}

func nonce(b []byte) error {
	_, err := crand.Read(b) // crypto/rand, bound as crand
	return err
}

go deeper

for a junior

Know that you call through the package name — rand.Intn after importing math/rand — and that writing a name before the path renames it for that file.

for a middle

Explain that the bound identifier comes from the imported package's package clause rather than the path, that imports are per file, and that an alias is mandatory when two paths would bind the same name.

for a senior

Judge when an alias helps and when it hurts: disambiguation yes, renaming a well-known package no. Be able to say concretely why dot imports are kept out of production code.

for a principal

Own the naming convention that removes most aliases in the first place — package names that read well at the call site. A codebase aliasing one package five ways is paying for a naming decision made elsewhere.

## Two different things wearing similar names An import declaration has two parts, and Go programmers routinely conflate them. The **import path** is a string literal. It tells the build system *which package* to compile in: `"math/rand"`, `"database/sql"`, `"example.com/toolkit/v2"`. It is not an identifier, it is not a name in your program, and you never write it anywhere else. The **package name** is the identifier the import introduces into your file. It comes from the imported package's own `package` clause — the first line of every file in that package. `math/rand` binds `rand` because those files begin with `package rand`. That they usually look the same is a convention, not a rule. The convention is strong and you should follow it in your own packages: name the package after the directory, keep it short, lowercase, no underscores, no camelCase. ## Where the two legitimately diverge - **Major-version path elements.** A module published at a `/v2` path has that element in its import path, but no package is called `v2`; the package clause still says something like `package toolkit`, and that is the identifier you get. - **Directory names that are not identifiers.** A directory named `data-store` cannot give its package that name, because a hyphen is not legal in a Go identifier. The clause will read `package datastore` or similar, and that is what binds. - **Deliberate mismatches.** Occasionally a package's directory is generic while its name is specific, or vice versa. The practical upshot: you cannot always predict the bound identifier from the path by eye. Read the package clause, or let the editor's import tooling — which resolves the real name — write the line for you. ## The alias form An import spec may carry an explicit name before the path: import crand "crypto/rand" This binds `crand` instead of the package's own name in this file only. There are three honest reasons to use it: 1. **Collision.** Two imported packages declare the same name. `crypto/rand` and `math/rand` are the canonical pair: both are `package rand`, and a file needing both must alias one. Without the alias the file does not compile. 2. **Clarity when the name is generic.** If the bound name is so generic that call sites read ambiguously, a short alias can help — sparingly. 3. **Adapting a mismatch** you find surprising, though this is usually worse than living with it. What an alias is *not* for is vanity: renaming a widely known package makes every reader translate. If a codebase aliases the same package five different ways, the problem is upstream naming, not the call sites. ## Imports are file-scoped An import binds a name in the **file block**. Consequences that people get wrong: - Every file that uses a package must import it, even if a sibling file in the same package already did. - An alias in one file has no effect in any other file, including files of the same package. - Two files of one package may alias the same path differently, which is legal and usually a smell. ## The two special forms **Blank import**, `import _ "path"`: binds no identifier at all. The package is still compiled, linked and initialised; you use this when the package's value is a registration side effect rather than an API you call, and it is also the only way to keep an import that no name references without tripping the unused-import error. **Dot import**, `import . "strings"`: the package's exported identifiers are declared directly in the importing file's scope, so you write `ToUpper(s)` instead of `strings.ToUpper(s)`. This is legal and almost always a mistake in production code, for three reasons: a reader can no longer tell where a bare name came from; adding an exported name upstream can suddenly collide with one of yours and break your build; and tooling that reasons about qualified names is weakened. Its defensible uses are narrow — generated code, and some example or test files whose whole point is to read as if from inside the package. ## Style summary Prefer no alias. Alias when the compiler forces you to, or when a reader genuinely benefits. Use blank imports deliberately and comment them. Treat dot imports as something you justify, not something you reach for.

  • What is the scope of an import declaration?
    The file block. Each file in a package must import what that file uses, even when a sibling file already imports it, and an alias written in one file has no effect in any other. Two files of the same package may legally bind the same path to different names, which usually signals inconsistent style rather than a real need.
  • When is a dot import defensible?
    Rarely — generated code, and some example or test files whose point is to read as if written inside the package. In ordinary code the costs dominate: a reader cannot tell which package a bare name came from, and a new exported name upstream can collide with a local one and break your build without any change on your side.
  • How would you know the bound name for a path you have never imported?
    Read the package clause of the imported package, or the first line of its documentation, which states the package name. You cannot always derive it from the path: a major-version element such as /v2 is never a package name, and a directory whose name contains a hyphen cannot be one either.

saying these in an interview costs you the question

  • Says the last path element is always the package name
  • Thinks an alias applies to the whole package, not one file
  • Treats dot imports as the normal way to shorten call sites
  • Believes two packages both named rand cannot be imported together