Question

Go Dot Imports: What a Period Means in an Import Statement

goimport

Question

In Go, packages are usually imported with their package path:

import (
    "fmt"
    "os"
    "launchpad.net/lpad"
)

However, this import uses a period before the package path:

import (
    "http"
    . "launchpad.net/gocheck"
    "launchpad.net/lpad"
    "os"
)

What does the . before "launchpad.net/gocheck" mean, and how does it affect the names that can be used in the importing Go file?

Short Answer

A dot import brings a package's exported identifiers directly into the current file's scope. This lets you write Assert(...) instead of gocheck.Assert(...). It is valid Go, but it is uncommon in production code because it makes it harder to see where a name came from.

Concept

Normally, importing a Go package gives it a package name that acts as a qualifier:

import "fmt"

fmt.Println("Hello")

Println belongs to the fmt package, so it is accessed as fmt.Println.

A dot import uses . as the imported package name:

import . "example.com/check"

It makes every exported identifier from check available directly in the importing file. For example, if check exports Assert, you can write:

Assert(value)

rather than:

check.Assert(value)

Only names beginning with an uppercase letter are exported in Go, so unexported package names are never made available by a dot import.

Dot imports affect only the single source file containing the import. They do not make names available throughout the entire package or project.

This feature is most often seen in tests or small domain-specific APIs, where short assertion names can improve readability. In general application code, explicit package qualifiers are preferred because they document the origin of a function, type, or variable.

Mental Model

Think of an ordinary import as keeping tools in a labeled toolbox:

fmt.Println("Hello")

The fmt. label tells you which toolbox Println came from.

A dot import takes the exported tools out of that toolbox and places them on your desk:

Println("Hello")

This is convenient, but if several toolboxes contain a tool named Println, it becomes unclear which tool you are using. That ambiguity is why dot imports should be used sparingly.

Take Quiz

Syntax and Examples

The general syntax for assigning an import name is:

import importName "package/path"

A dot import uses . as that name:

import . "example.com/check"

Suppose a package named check exports this function:

package check

func Assert(ok bool, message string) {
    if !ok {
        panic(message)
    }
}

With a normal import, qualify the function with the package name:

package main

import "example.com/check"

func main() {
    check.Assert(2+2 == 4, "math is incorrect")
}

With a dot import, use the exported function directly:

package main

import . "example.com/check"

func main() {
    Assert(2+2 == 4, "math is incorrect")
}

The two calls invoke the same function. The only difference is how its name is written.

A dot import does not expose lowercase names:

// This would fail if helper is unexported in check.
helper()

Only exported names, such as Assert, Checker, or NewSuite, can be used.

Step by Step Execution

Consider this test-style code:

package calculator_test

import . "example.com/check"

func TestAdd(t *T) {
    result := 2 + 3
    Assert(result == 5, "unexpected result")
}

Step by step:

  1. Go reads the import declaration.
  2. The . tells Go to make exported identifiers from example.com/check available in this file's file scope.
  3. T is resolved as an exported type from the imported package, equivalent to check.T with a normal import.
  4. Assert is resolved as an exported function from the imported package, equivalent to check.Assert.
  5. Go calls that package's Assert function with the boolean result and message.

The equivalent code without a dot import is clearer about each name's source:

package calculator_test

import "example.com/check"

func TestAdd(t *check.T) {
    result := 2 + 3
    check.Assert(result == 5, "unexpected result")
}

Real World Use Cases

Dot imports are uncommon, but they can appear in a few situations:

  • Test assertion libraries: A test framework may provide concise names such as Assert, Equals, or IsNil. Using them repeatedly can be readable in a focused test file.
  • Generated code: A code generator may control all imports and identifiers, making naming conflicts predictable.
  • Small, carefully controlled DSLs: A package designed as a mini-language may intentionally use a dot import to make expressions read naturally.

For example, older Go testing libraries sometimes used patterns like this:

import . "launchpad.net/gocheck"

func (s *Suite) TestValue(c *C) {
    c.Assert(10, Equals, 10)
}

Here, names such as C and Equals can be used without a gocheck. prefix. The intent is concise test syntax.

Take Quiz

Real Codebase Usage

In most Go codebases, developers use normal imports:

import (
    "fmt"
    "net/http"
)

func handle(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "OK")
}

The prefixes http. and fmt. make dependencies obvious during reviews and maintenance.

Common practical patterns are:

  • Use an import alias when two packages have the same package name or when a clearer local name helps.

    import stdlog "log"
    
  • Use normal qualified names for public application logic.

    validator.Validate(input)
    
  • Reserve dot imports for narrowly scoped test files, if the team accepts the style and the imported API is intentionally DSL-like.

  • Avoid collisions by keeping package prefixes. This is particularly valuable when a project imports packages that might both export names such as Error, Client, Config, or New.

A package imported with . is still initialized in the normal Go import initialization process, just like an ordinary import.

Common Mistakes

Expecting dot-imported names in other files

A dot import applies only to the file that declares it.

// file_a.go
import . "example.com/check"

func testA() {
    Assert(true, "works here")
}
// file_b.go
func testB() {
    Assert(true, "does not compile")
}

To use Assert in file_b.go, that file also needs the relevant import, or it should use a normal qualified import.

Forgetting that only exported names are imported

Lowercase names are package-private:

import . "example.com/check"

// helper() does not compile if helper begins with lowercase in check.
// helper()

Use exported names only, which begin with an uppercase letter.

Creating name collisions

A dot import can conflict with a name already declared in the file or imported from another dot-imported package.

import (
    . "example.com/first"
    . "example.com/second"
)

// This may fail if both packages export Result.
var value Result

Avoid this by using package names or aliases:

import (
    first "example.com/first"
    second "example.com/second"
)

var a first.Result
var b second.Result

Making code harder to read

This code gives no immediate clue about where NewClient comes from:

client := NewClient()

The explicit version is easier to understand:

client := api.NewClient()

Confusing . with _

These are different import forms. . imports exported names into the current file. _ imports a package only for its side effects, typically its init function.

Comparisons

Import formExampleHow names are usedTypical purpose
Normal importimport "fmt"fmt.Println()Default choice for most code
Aliased importimport f "fmt"f.Println()Resolve conflicts or use a clearer short name
Dot importimport . "example.com/check"Assert(...)Rare; mostly controlled test or DSL code
Blank importimport _ "example.com/driver"Names cannot be usedRun package initialization for registration or side effects

A normal import preserves the package boundary in the code. A dot import removes that visible qualifier for exported names. A blank import does not make any identifiers available at all.

Take Quiz

Cheat Sheet

// Normal import
import "fmt"
fmt.Println("Hi")

// Import with an alias
import f "fmt"
f.Println("Hi")

// Dot import: exported names enter this file's scope
import . "example.com/check"
Assert(true, "must be true")

// Blank import: initialization only; no names available
import _ "example.com/driver"

Key rules:

  • . means import all exported identifiers into the current file's scope.
  • . does not import lowercase, unexported identifiers.
  • The effect is limited to the file containing the import.
  • Dot imports can cause naming conflicts and hide an identifier's origin.
  • Prefer normal imports unless a controlled test or DSL use case clearly benefits from a dot import.
  • _ and . are not interchangeable: _ imports no usable names.

FAQ

What does a dot import do in Go?

A dot import makes the imported package's exported identifiers available without a package prefix in the current source file.

Does a dot import import private functions in Go?

No. It only exposes exported identifiers, which conventionally start with an uppercase letter.

Is . import the same as a blank identifier import?

No. import . "pkg" makes exported names directly usable. import _ "pkg" makes no names usable and is used only for package initialization side effects.

Are Go dot imports allowed?

Yes. They are part of Go's import syntax. However, they are generally discouraged outside narrow, intentional cases because they reduce clarity.

Why are dot imports often used in Go tests?

Some assertion libraries expose many short, related names. A dot import can make test assertions less repetitive, although explicit package names remain common and clearer.

Does a dot import affect every file in the package?

No. Imports are declared per file. A dot-imported name is available only in the file where that import appears.

What happens if a dot import conflicts with another name?

The file will not compile if Go cannot resolve the name uniquely or if the imported name conflicts with a declaration in the same scope. Use normal imports or aliases instead.

Take Quiz

Mini Project

Description

Build a small Go module with a check package and a test-like application. First use a dot import to see how an exported assertion function is called without a prefix. Then compare it with a normal import, which is usually easier to maintain.

Goal

Demonstrate that a dot import exposes exported package identifiers only within the importing file.

Requirements

Create a check package that exports an Assert function. Create a main package that imports check using .. Call Assert without writing a package prefix. Run the program with a passing condition. Change the condition to false to observe the failure message.

Take Quiz

Keep learning

Related questions

View more